Merge remote-tracking branch 'origin/corelib_2_2_1_branch'

This doesn't change the state of master. An old version of the Dart
SDK depended on a commit from the branch that's being merged in, and
this ensures that that commit remains reachable so that version can
still be built.
diff --git a/.travis.yml b/.travis.yml
index a24a813..3ef6c26 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,10 @@
 language: dart
 sudo: false
-dart:
-  - dev
-  - stable
+dart: dev
 
 dart_task:
   - test: --platform vm
   - test: --platform firefox -j 1
-  - test: --platform dartium
-    install_dartium: true
   - dartanalyzer
 
 matrix:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49b2eec..d50f8d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,6 @@
 ## 1.0.7
 
-* Updates to support Dart 2.0 core library changes (wave 2.2). 
-  See [issue 31847][sdk#31847] for details.
-
-  [sdk#31847]: https://github.com/dart-lang/sdk/issues/31847
+* Support the latest dev SDK.
 
 ## 1.0.6
 
diff --git a/lib/src/copy/bytes_builder.dart b/lib/src/copy/bytes_builder.dart
index 429e6d1..1e37093 100644
--- a/lib/src/copy/bytes_builder.dart
+++ b/lib/src/copy/bytes_builder.dart
@@ -9,9 +9,8 @@
 // Because it's copied directly, there are no modifications from the original.
 //
 // This is up-to-date as of sdk revision
-// e41fb4cafd6052157dbc1490d437045240f4773f.
+// 365f7b5a8b6ef900a5ee23913b7203569b81b175.
 
-import 'dart:math';
 import 'dart:typed_data';
 
 /// Builds a list of bytes, allowing bytes and lists of bytes to be added at the
@@ -46,7 +45,7 @@
 
   /// Returns the contents of `this` and clears `this`.
   ///
-  /// The list returned is a view of the the internal buffer, limited to the
+  /// The list returned is a view of the internal buffer, limited to the
   /// [length].
   List<int> takeBytes();
 
@@ -72,24 +71,22 @@
   // 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) {
@@ -103,18 +100,39 @@
   }
 
   void addByte(int byte) {
-    add([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));
   }
@@ -127,10 +145,11 @@
 
   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;
@@ -143,24 +162,28 @@
 
 class _BytesBuilder implements BytesBuilder {
   int _length = 0;
-  final _chunks = <List<int>>[];
+  final List<Uint8List> _chunks = [];
 
   void add(List<int> bytes) {
-    if (bytes is! Uint8List) {
-      bytes = new Uint8List.fromList(bytes);
+    Uint8List typedBytes;
+    if (bytes is Uint8List) {
+      typedBytes = bytes;
+    } else {
+      typedBytes = new Uint8List.fromList(bytes);
     }
-    _chunks.add(bytes);
-    _length += bytes.length;
+    _chunks.add(typedBytes);
+    _length += typedBytes.length;
   }
 
   void addByte(int byte) {
-    add([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;
     }
@@ -175,7 +198,7 @@
   }
 
   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) {
diff --git a/lib/src/copy/io_sink.dart b/lib/src/copy/io_sink.dart
index dafd86b..3a51ff1 100644
--- a/lib/src/copy/io_sink.dart
+++ b/lib/src/copy/io_sink.dart
@@ -9,7 +9,7 @@
 // desired public API and to remove "dart:io" dependencies have been made.
 //
 // This is up-to-date as of sdk revision
-// e41fb4cafd6052157dbc1490d437045240f4773f.
+// 365f7b5a8b6ef900a5ee23913b7203569b81b175.
 
 import 'dart:async';
 
@@ -24,12 +24,20 @@
 
   StreamSinkImpl(this._target);
 
+  // The _reportClosedSink method has been deleted for web_socket_channel. This
+  // method did nothing but print to stderr, which is unavailable here.
+
   void add(T data) {
-    if (_isClosed) return;
+    if (_isClosed) {
+      return;
+    }
     _controller.add(data);
   }
 
   void addError(error, [StackTrace stackTrace]) {
+    if (_isClosed) {
+      return;
+    }
     _controller.addError(error, stackTrace);
   }
 
@@ -37,19 +45,19 @@
     if (_isBound) {
       throw new StateError("StreamSink is already bound to a stream");
     }
-    _isBound = true;
     if (_hasError) return done;
-    // Wait for any sync operations to complete.
-    Future targetAddStream() {
-      return _target.addStream(stream).whenComplete(() {
-        _isBound = false;
-      });
-    }
 
-    if (_controllerInstance == null) return targetAddStream();
-    var future = _controllerCompleter.future;
-    _controllerInstance.close();
-    return future.then((_) => targetAddStream());
+    _isBound = true;
+    var future = _controllerCompleter == null
+        ? _target.addStream(stream)
+        : _controllerCompleter.future.then((_) => _target.addStream(stream));
+    _controllerInstance?.close();
+
+    // Wait for any pending events in [_controller] to be dispatched before
+    // adding [stream].
+    return future.whenComplete(() {
+      _isBound = false;
+    });
   }
 
   Future flush() {
diff --git a/lib/src/copy/web_socket.dart b/lib/src/copy/web_socket.dart
index c08b9ac..1928a34 100644
--- a/lib/src/copy/web_socket.dart
+++ b/lib/src/copy/web_socket.dart
@@ -9,7 +9,7 @@
 // desired public API and to remove "dart:io" dependencies have been made.
 //
 // This is up-to-date as of sdk revision
-// e41fb4cafd6052157dbc1490d437045240f4773f.
+// 365f7b5a8b6ef900a5ee23913b7203569b81b175.
 
 /// Web socket status codes used when closing a web socket connection.
 abstract class WebSocketStatus {
diff --git a/lib/src/copy/web_socket_impl.dart b/lib/src/copy/web_socket_impl.dart
index 5c8e1b3..58bcaba 100644
--- a/lib/src/copy/web_socket_impl.dart
+++ b/lib/src/copy/web_socket_impl.dart
@@ -10,7 +10,7 @@
 // desired public API and to remove "dart:io" dependencies have been made.
 //
 // This is up-to-date as of sdk revision
-// e41fb4cafd6052157dbc1490d437045240f4773f.
+// 365f7b5a8b6ef900a5ee23913b7203569b81b175.
 
 import 'dart:async';
 import 'dart:convert';
@@ -57,16 +57,20 @@
   static const int RESERVED_F = 15;
 }
 
+class _EncodedString {
+  final List<int> bytes;
+  _EncodedString(this.bytes);
+}
+
 /// The web socket protocol transformer handles the protocol byte stream
-/// which is supplied through the [:handleData:]. As the protocol is processed,
+/// which is supplied through the `handleData`. As the protocol is processed,
 /// it'll output frame data as either a List<int> or String.
 ///
 /// Important information about usage: Be sure you use cancelOnError, so the
 /// socket will be closed when the processor encounter an error. Not using it
 /// will lead to undefined behaviour.
-// TODO(ajohnsen): make this transformer reusable?
-class _WebSocketProtocolTransformer
-    extends StreamTransformerBase<List<int>, dynamic>
+class _WebSocketProtocolTransformer extends StreamTransformerBase<List<int>,
+        dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ >
     implements EventSink<List<int>> {
   static const int START = 0;
   static const int LEN_FIRST = 1;
@@ -94,7 +98,7 @@
   int closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
   String closeReason = "";
 
-  EventSink _eventSink;
+  EventSink<dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ > _eventSink;
 
   final bool _serverSide;
   final List _maskingBytes = new List(4);
@@ -102,7 +106,8 @@
 
   _WebSocketProtocolTransformer([this._serverSide = false]);
 
-  Stream bind(Stream stream) {
+  Stream<dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ > bind(
+      Stream<List<int>> stream) {
     return new Stream.eventTransformed(stream, (EventSink eventSink) {
       if (_eventSink != null) {
         throw new StateError("WebSocket transformer already used.");
@@ -320,7 +325,7 @@
 
       switch (_currentMessageType) {
         case _WebSocketMessageType.TEXT:
-          _eventSink.add(UTF8.decode(bytes));
+          _eventSink.add(utf8.decode(bytes));
           break;
         case _WebSocketMessageType.BINARY:
           _eventSink.add(bytes);
@@ -345,7 +350,7 @@
             throw new WebSocketChannelException("Protocol error");
           }
           if (payload.length > 2) {
-            closeReason = UTF8.decode(payload.sublist(2));
+            closeReason = utf8.decode(payload.sublist(2));
           }
         }
         _state = CLOSED;
@@ -400,7 +405,8 @@
   _WebSocketOutgoingTransformer(this.webSocket);
 
   Stream<List<int>> bind(Stream stream) {
-    return new Stream.eventTransformed(stream, (eventSink) {
+    return new Stream<List<int>>.eventTransformed(stream,
+        (EventSink<List<int>> eventSink) {
       if (_eventSink != null) {
         throw new StateError("WebSocket transformer already used");
       }
@@ -423,14 +429,15 @@
     if (message != null) {
       if (message is String) {
         opcode = _WebSocketOpcode.TEXT;
-        data = UTF8.encode(message);
+        data = utf8.encode(message);
+      } else if (message is List<int>) {
+        opcode = _WebSocketOpcode.BINARY;
+        data = message;
+      } else if (message is _EncodedString) {
+        opcode = _WebSocketOpcode.TEXT;
+        data = message.bytes;
       } else {
-        if (message is List<int>) {
-          data = message;
-          opcode = _WebSocketOpcode.BINARY;
-        } else {
-          throw new ArgumentError(message);
-        }
+        throw new ArgumentError(message);
       }
     } else {
       opcode = _WebSocketOpcode.TEXT;
@@ -451,17 +458,24 @@
       data.add((code >> 8) & 0xFF);
       data.add(code & 0xFF);
       if (reason != null) {
-        data.addAll(UTF8.encode(reason));
+        data.addAll(utf8.encode(reason));
       }
     }
     addFrame(_WebSocketOpcode.CLOSE, data);
     _eventSink.close();
   }
 
-  void addFrame(int opcode, List<int> data) =>
-      createFrame(opcode, data, webSocket._serverSide, false).forEach((e) {
-        _eventSink.add(e);
-      });
+  void addFrame(int opcode, List<int> data) {
+    createFrame(
+        opcode,
+        data,
+        webSocket._serverSide,
+        // Logic around _deflateHelper was removed here, since ther ewill never
+        // be a deflate helper for a cross-platform WebSocket client.
+        false).forEach((e) {
+      _eventSink.add(e);
+    });
+  }
 
   static Iterable<List<int>> createFrame(
       int opcode, List<int> data, bool serverSide, bool compressed) {
@@ -564,7 +578,7 @@
   StreamSubscription _subscription;
   bool _issuedPause = false;
   bool _closed = false;
-  final Completer _closeCompleter = new Completer();
+  final Completer _closeCompleter = new Completer<WebSocketImpl>();
   Completer _completer;
 
   _WebSocketConsumer(this.webSocket, this.sink);
diff --git a/pubspec.yaml b/pubspec.yaml
index 08dde58..dd70f7e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -5,7 +5,7 @@
 homepage: https://github.com/dart-lang/web_socket_channel
 
 environment:
-  sdk: '>=2.0.0-dev.20.0 <2.0.0'
+  sdk: '>=2.0.0-dev.23.0 <2.0.0'
 
 dependencies:
   async: '>=1.3.0 <3.0.0'