diff --git a/docs/language/dartLangSpec.tex b/docs/language/dartLangSpec.tex index 6edabf22e03..b0c749c6385 100644 --- a/docs/language/dartLangSpec.tex +++ b/docs/language/dartLangSpec.tex @@ -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{}. %} diff --git a/sdk/lib/io/bytes_builder.dart b/sdk/lib/io/bytes_builder.dart index 2dca39d0c18..1b61d30f4fa 100644 --- a/sdk/lib/io/bytes_builder.dart +++ b/sdk/lib/io/bytes_builder.dart @@ -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 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 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 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 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 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) { diff --git a/sdk/lib/io/http_headers.dart b/sdk/lib/io/http_headers.dart index 3769b3f0d04..40df48a3c2f 100644 --- a/sdk/lib/io/http_headers.dart +++ b/sdk/lib/io/http_headers.dart @@ -465,42 +465,32 @@ class _HttpHeaders implements HttpHeaders { _mutable = false; } - int _write(Uint8List buffer, int offset) { - void write(List 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 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() { diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart index a965afa43fe..ac7d67a7ef5 100644 --- a/sdk/lib/io/http_impl.dart +++ b/sdk/lib/io/http_impl.dart @@ -574,29 +574,20 @@ class _HttpResponse extends _HttpOutboundMessage } void _writeHeader() { - Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); - int offset = 0; - - void write(List 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 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 } void _writeHeader() { - Uint8List buffer = new Uint8List(_OUTGOING_BUFFER_SIZE); - int offset = 0; - - void write(List 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 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> { // 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> { } 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> { void setHeader(List data, int length) { assert(_length == 0); - assert(data.length == _OUTGOING_BUFFER_SIZE); _buffer = data; _length = length; } diff --git a/tests/standalone/io/http_client_request_test.dart b/tests/standalone/io/http_client_request_test.dart index 9015b9f4abf..cd2a7beb045 100644 --- a/tests/standalone/io/http_client_request_test.dart +++ b/tests/standalone/io/http_client_request_test.dart @@ -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(); } diff --git a/tests/standalone/io/http_server_response_test.dart b/tests/standalone/io/http_server_response_test.dart index c3c0afb51d6..a6c06aff340 100644 --- a/tests/standalone/io/http_server_response_test.dart +++ b/tests/standalone/io/http_server_response_test.dart @@ -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(); }