Specify FutureOr.

R=eernst@google.com, floitsch@google.com

Review-Url: https://codereview.chromium.org/2664263002 .
This commit is contained in:
Lasse Reichstein Holst Nielsen
2017-02-03 08:02:09 +01:00
parent 7512ab5c79
commit c41ae15713
6 changed files with 126 additions and 148 deletions
+26 -2
View File
@@ -28,6 +28,7 @@
% - Assert statements may now also include a "message" operand.
% - The Null type is now considered a subtype of all types in most cases.
% - Specify what NEWLINE means in multiline strings.
% - Specified the FutureOf type.
%
% 1.14
% - The call "C()" where "C" is a class name, is a now compile-time error.
@@ -7228,11 +7229,11 @@ Static type annotations are used in variable declarations (\ref{variables}) (inc
.
{\bf typeArguments:}
'<' typeList '>'
`<' typeList `>'
.
{\bf typeList:}
type (',' type)*
type (`,' type)*
.
\end{grammar}
@@ -7653,6 +7654,29 @@ What of static checking? Surely we would want to flag (2) when users have explic
\LMHash{}
The name \DYNAMIC{} denotes a \cd{Type} object even though \DYNAMIC{} is not a class.
\LMHash{}
The built-in type declaration \code{FutureOr},
which is declared in the library \code{dart:async},
defines a generic type with one type parameter (\ref{generics}).
If the \code{FutureOr} type, optionally followed by type arguments,
is used as a type, it denotes the \DYNAMIC{} type.
As such, the \code{FutureOr} type cannot be used where \DYNAMIC{} cannot be
used as a type.
If the type arguments applied to \code{FutureOr} would issue static warnings
if applied to a normal generic class with one type parameter,
the same warnings are issued for \code{FutureOr},
even though the type arguments are otherwise ignored.
The name \code{FutureOr} as an expression
denotes the same \cd{Type} object as \DYNAMIC{}.
\rationale{
The \code{FutureOr} type is reserved for future use,
for cases where a value can be either an instance of the type \metavar{type}
or the type \code{Future<\metavar{type}>}. Such cases occur naturally
in asynchronous code.
Using \code{FutureOr} instead of \DYNAMIC{} will allow some tools
to provide a more precise type analysis.
}
%\rationale {
%Type objects reify the runtime types of instances. No instance ever has type \DYNAMIC{}.
%}
+45 -20
View File
@@ -83,24 +83,22 @@ class _CopyingBytesBuilder implements BytesBuilder {
// Start with 1024 bytes.
static const int _INIT_SIZE = 1024;
static final _emptyList = new Uint8List(0);
int _length = 0;
Uint8List _buffer;
_CopyingBytesBuilder([int initialCapacity = 0])
: _buffer = (initialCapacity <= 0)
? _emptyList
: new Uint8List(_pow2roundup(initialCapacity));
void add(List<int> bytes) {
int bytesLength = bytes.length;
if (bytesLength == 0) return;
int required = _length + bytesLength;
if (_buffer == null) {
int size = _pow2roundup(required);
size = max(size, _INIT_SIZE);
_buffer = new Uint8List(size);
} else if (_buffer.length < required) {
// We will create a list in the range of 2-4 times larger than
// required.
int size = _pow2roundup(required) * 2;
var newBuffer = new Uint8List(size);
newBuffer.setRange(0, _buffer.length, _buffer);
_buffer = newBuffer;
if (_buffer.length < required) {
_grow(required);
}
assert(_buffer.length >= required);
if (bytes is Uint8List) {
@@ -113,17 +111,40 @@ class _CopyingBytesBuilder implements BytesBuilder {
_length = required;
}
void addByte(int byte) { add([byte]); }
void addByte(int byte) {
if (_buffer.length == _length) {
// The grow algorithm always at least doubles.
// If we added one to _length it would quadruple unnecessarily.
_grow(_length);
}
assert(_buffer.length > _length);
_buffer[_length] = byte;
_length++;
}
void _grow(int required) {
// We will create a list in the range of 2-4 times larger than
// required.
int newSize = required * 2;
if (newSize < _INIT_SIZE) {
newSize = _INIT_SIZE;
} else {
newSize = _pow2roundup(newSize);
}
var newBuffer = new Uint8List(newSize);
newBuffer.setRange(0, _buffer.length, _buffer);
_buffer = newBuffer;
}
List<int> takeBytes() {
if (_buffer == null) return new Uint8List(0);
if (_length == 0) return _emptyList;
var buffer = new Uint8List.view(_buffer.buffer, 0, _length);
clear();
return buffer;
}
List<int> toBytes() {
if (_buffer == null) return new Uint8List(0);
if (_length == 0) return _emptyList;
return new Uint8List.fromList(
new Uint8List.view(_buffer.buffer, 0, _length));
}
@@ -136,10 +157,11 @@ class _CopyingBytesBuilder implements BytesBuilder {
void clear() {
_length = 0;
_buffer = null;
_buffer = _emptyList;
}
int _pow2roundup(int x) {
static int _pow2roundup(int x) {
assert(x > 0);
--x;
x |= x >> 1;
x |= x >> 2;
@@ -166,12 +188,15 @@ class _BytesBuilder implements BytesBuilder {
_length += typedBytes.length;
}
void addByte(int byte) { add([byte]); }
void addByte(int byte) {
_chunks.add(new Uint8List(1)..[0] = byte);
_length++;
}
List<int> takeBytes() {
if (_chunks.length == 0) return new Uint8List(0);
if (_length == 0) return _CopyingBytesBuilder._emptyList;
if (_chunks.length == 1) {
var buffer = _chunks.single;
var buffer = _chunks[0];
clear();
return buffer;
}
@@ -186,7 +211,7 @@ class _BytesBuilder implements BytesBuilder {
}
List<int> toBytes() {
if (_chunks.length == 0) return new Uint8List(0);
if (_length == 0) return _CopyingBytesBuilder._emptyList;
var buffer = new Uint8List(_length);
int offset = 0;
for (var chunk in _chunks) {
+14 -24
View File
@@ -465,42 +465,32 @@ class _HttpHeaders implements HttpHeaders {
_mutable = false;
}
int _write(Uint8List buffer, int offset) {
void write(List<int> bytes) {
int len = bytes.length;
for (int i = 0; i < len; i++) {
buffer[offset + i] = bytes[i];
}
offset += len;
}
// Format headers.
void _build(BytesBuilder builder) {
for (String name in _headers.keys) {
List<String> values = _headers[name];
bool fold = _foldHeader(name);
var nameData = name.codeUnits;
write(nameData);
buffer[offset++] = _CharCode.COLON;
buffer[offset++] = _CharCode.SP;
builder.add(nameData);
builder.addByte(_CharCode.COLON);
builder.addByte(_CharCode.SP);
for (int i = 0; i < values.length; i++) {
if (i > 0) {
if (fold) {
buffer[offset++] = _CharCode.COMMA;
buffer[offset++] = _CharCode.SP;
builder.addByte(_CharCode.COMMA);
builder.addByte(_CharCode.SP);
} else {
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
write(nameData);
buffer[offset++] = _CharCode.COLON;
buffer[offset++] = _CharCode.SP;
builder.addByte(_CharCode.CR);
builder.addByte(_CharCode.LF);
builder.add(nameData);
builder.addByte(_CharCode.COLON);
builder.addByte(_CharCode.SP);
}
}
write(values[i].codeUnits);
builder.add(values[i].codeUnits);
}
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
builder.addByte(_CharCode.CR);
builder.addByte(_CharCode.LF);
}
return offset;
}
String toString() {
+41 -70
View File
@@ -574,29 +574,20 @@ class _HttpResponse extends _HttpOutboundMessage<HttpResponse>
}
void _writeHeader() {
Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
int offset = 0;
void write(List<int> bytes) {
int len = bytes.length;
for (int i = 0; i < len; i++) {
buffer[offset + i] = bytes[i];
}
offset += len;
}
BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
// Write status line.
if (headers.protocolVersion == "1.1") {
write(_Const.HTTP11);
buffer.add(_Const.HTTP11);
} else {
write(_Const.HTTP10);
buffer.add(_Const.HTTP10);
}
buffer[offset++] = _CharCode.SP;
write(statusCode.toString().codeUnits);
buffer[offset++] = _CharCode.SP;
write(reasonPhrase.codeUnits);
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
buffer.addByte(_CharCode.SP);
buffer.add(statusCode.toString().codeUnits);
buffer.addByte(_CharCode.SP);
buffer.add(reasonPhrase.codeUnits);
buffer.addByte(_CharCode.CR);
buffer.addByte(_CharCode.LF);
var session = _httpRequest._session;
if (session != null && !session._destroyed) {
@@ -630,10 +621,11 @@ class _HttpResponse extends _HttpOutboundMessage<HttpResponse>
headers._finalize();
// Write headers.
offset = headers._write(buffer, offset);
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
_outgoing.setHeader(buffer, offset);
headers._build(buffer);
buffer.addByte(_CharCode.CR);
buffer.addByte(_CharCode.LF);
Uint8List headerBytes = buffer.takeBytes();
_outgoing.setHeader(headerBytes, headerBytes.length);
}
String _findReasonPhrase(int statusCode) {
@@ -821,27 +813,18 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
}
void _writeHeader() {
Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
int offset = 0;
void write(List<int> bytes) {
int len = bytes.length;
for (int i = 0; i < len; i++) {
buffer[offset + i] = bytes[i];
}
offset += len;
}
BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
// Write the request method.
write(method.codeUnits);
buffer[offset++] = _CharCode.SP;
buffer.add(method.codeUnits);
buffer.addByte(_CharCode.SP);
// Write the request URI.
write(_requestUri().codeUnits);
buffer[offset++] = _CharCode.SP;
buffer.add(_requestUri().codeUnits);
buffer.addByte(_CharCode.SP);
// Write HTTP/1.1.
write(_Const.HTTP11);
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
buffer.add(_Const.HTTP11);
buffer.addByte(_CharCode.CR);
buffer.addByte(_CharCode.LF);
// Add the cookies to the headers.
if (!cookies.isEmpty) {
@@ -856,10 +839,11 @@ class _HttpClientRequest extends _HttpOutboundMessage<HttpClientResponse>
headers._finalize();
// Write headers.
offset = headers._write(buffer, offset);
buffer[offset++] = _CharCode.CR;
buffer[offset++] = _CharCode.LF;
_outgoing.setHeader(buffer, offset);
headers._build(buffer);
buffer.addByte(_CharCode.CR);
buffer.addByte(_CharCode.LF);
Uint8List headerBytes = buffer.takeBytes();
_outgoing.setHeader(headerBytes, headerBytes.length);
}
}
@@ -935,18 +919,6 @@ class _HttpOutgoing implements StreamConsumer<List<int>> {
// Returns either a future or 'null', if it was able to write headers
// immediately.
Future writeHeaders({bool drainRequest: true, bool setOutgoing: true}) {
Future write() {
try {
outbound._writeHeader();
} catch (_) {
// Headers too large.
return new Future.error(new HttpException(
"Headers size exceeded the of '$_OUTGOING_BUFFER_SIZE'"
" bytes"));
}
return null;
}
if (headersWritten) return null;
headersWritten = true;
Future drainFuture;
@@ -975,22 +947,22 @@ class _HttpOutgoing implements StreamConsumer<List<int>> {
} else {
drainRequest = false;
}
if (ignoreBody) {
return write();
}
if (setOutgoing) {
int contentLength = outbound.headers.contentLength;
if (outbound.headers.chunkedTransferEncoding) {
chunked = true;
if (gzip) this.gzip = true;
} else if (contentLength >= 0) {
this.contentLength = contentLength;
if (!ignoreBody) {
if (setOutgoing) {
int contentLength = outbound.headers.contentLength;
if (outbound.headers.chunkedTransferEncoding) {
chunked = true;
if (gzip) this.gzip = true;
} else if (contentLength >= 0) {
this.contentLength = contentLength;
}
}
if (drainFuture != null) {
return drainFuture.then((_) => outbound._writeHeader());
}
}
if (drainFuture != null) {
return drainFuture.then((_) => write());
}
return write();
outbound._writeHeader();
return null;
}
@@ -1160,7 +1132,6 @@ class _HttpOutgoing implements StreamConsumer<List<int>> {
void setHeader(List<int> data, int length) {
assert(_length == 0);
assert(data.length == _OUTGOING_BUFFER_SIZE);
_buffer = data;
_length = length;
}
@@ -108,25 +108,8 @@ void testBadResponseClose() {
}
void testBadHeaders() {
asyncStart();
testClientRequest((request) {
var value = "a";
for (int i = 0; i < 8 * 1024; i++) {
value += 'a';
}
request.headers.set('name', value);
request.done.catchError((error) {
asyncEnd();
}, test: (e) => e is HttpException);
return request.close();
});
}
void main() {
testResponseDone();
testBadResponseAdd();
testBadResponseClose();
testBadHeaders();
}
@@ -281,20 +281,6 @@ void testIgnoreRequestData() {
}
void testBadHeaders() {
testServerRequest((server, request) {
var value = "a";
for (int i = 0; i < 8 * 1024; i++) {
value += 'a';
}
request.response.headers.set('name', value);
request.response.close().catchError((error) {
server.close();
}, test: (e) => e is HttpException);
});
}
void testWriteCharCode() {
testServerRequest((server, request) {
// Test that default is latin-1 (only 2 bytes).
@@ -315,6 +301,5 @@ void main() {
testBadResponseAdd();
testBadResponseClose();
testIgnoreRequestData();
testBadHeaders();
testWriteCharCode();
}