Specify FutureOr.
R=eernst@google.com, floitsch@google.com Review-Url: https://codereview.chromium.org/2664263002 .
This commit is contained in:
@@ -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{}.
|
||||
%}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user