Version 2.10.0-110.0.dev

Merge commit '4d72ed38b80a7d6d377b92747868dbd392a058c5' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 30cd1fc..08fc72b 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -96,6 +96,7 @@
     LintNames.prefer_equal_for_default_values:
         ReplaceColonWithEquals.newInstance,
     LintNames.prefer_final_fields: MakeFinal.newInstance,
+    LintNames.prefer_final_locals: MakeFinal.newInstance,
     LintNames.prefer_for_elements_to_map_fromIterable:
         ConvertMapFromIterableToForLiteral.newInstance,
     LintNames.prefer_generic_function_type_aliases:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index 0690f27..b81936e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/modify_parameters.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
@@ -17,11 +18,13 @@
 
 /// A parser used to read a transform set from a file.
 class TransformSetParser {
+  static const String _argumentValueKey = 'argumentValue';
   static const String _changesKey = 'changes';
   static const String _classKey = 'class';
+  static const String _constantKey = 'constant';
   static const String _constructorKey = 'constructor';
+  static const String _defaultValueKey = 'defaultValue';
   static const String _elementKey = 'element';
-  static const String _enumConstantKey = 'constant';
   static const String _enumKey = 'enum';
   static const String _extensionKey = 'extension';
   static const String _fieldKey = 'field';
@@ -38,6 +41,7 @@
   static const String _nameKey = 'name';
   static const String _newNameKey = 'newName';
   static const String _setterKey = 'setter';
+  static const String _styleKey = 'style';
   static const String _titleKey = 'title';
   static const String _transformsKey = 'transforms';
   static const String _typedefKey = 'typedef';
@@ -49,15 +53,17 @@
   /// the possible containers of that element.
   static const Map<String, List<String>> _containerKeyMap = {
     _constructorKey: [_inClassKey],
-    _enumConstantKey: [_inEnumKey],
+    _constantKey: [_inEnumKey],
     _fieldKey: [_inClassKey, _inExtensionKey, _inMixinKey],
     _getterKey: [_inClassKey, _inExtensionKey, _inMixinKey],
     _methodKey: [_inClassKey, _inExtensionKey, _inMixinKey],
     _setterKey: [_inClassKey, _inExtensionKey, _inMixinKey],
   };
 
+  static const String _addParameterKind = 'addParameter';
   static const String _addTypeParameterKind = 'addTypeParameter';
   static const String _argumentKind = 'argument';
+  static const String _removeParameterKind = 'removeParameter';
   static const String _renameKind = 'rename';
 
   static const int currentVersion = 1;
@@ -65,6 +71,10 @@
   /// The error reporter to which diagnostics will be reported.
   final ErrorReporter errorReporter;
 
+  /// The parameter modifications associated with the current transform, or
+  /// `null` if the current transform does not yet have any such modifications.
+  List<ParameterModification> _parameterModifications;
+
   /// Initialize a newly created parser to report diagnostics to the
   /// [errorReporter].
   TransformSetParser(this.errorReporter);
@@ -160,6 +170,52 @@
     return foundKeys[0];
   }
 
+  /// Translate the [node] into a add-parameter modification.
+  void _translateAddParameterChange(YamlMap node) {
+    _singleKey(node, [_indexKey, _nameKey]);
+    _reportUnsupportedKeys(node, const {_indexKey, _kindKey, _nameKey});
+    var index = _translateInteger(node.valueAt(_indexKey), _indexKey);
+    if (index == null) {
+      return;
+    }
+    var name = _translateString(node.valueAt(_nameKey), _nameKey);
+    if (name == null) {
+      return;
+    }
+    var style = _translateString(node.valueAt(_styleKey), _styleKey);
+    if (style == null) {
+      return;
+    }
+    var isRequired = style.startsWith('required_');
+    var isPositional = style.endsWith('_positional');
+    // TODO(brianwilkerson) I originally thought we'd need a default value, but
+    //  it seems like we ought to be able to get it from the overridden method,
+    //  so investigate removing this field.
+    var defaultValue = _translateValueExtractor(
+        node.valueAt(_defaultValueKey), _defaultValueKey);
+    if (isRequired && defaultValue != null) {
+      // TODO(brianwilkerson) Report that required parameters can't have a
+      //  default value.
+      return;
+    }
+    var argumentValue = _translateValueExtractor(
+        node.valueAt(_argumentValueKey), _argumentValueKey);
+    // TODO(brianwilkerson) We really ought to require an argument value for
+    //  optional positional parameters too for the case where the added
+    //  parameter is being added before the end of the list and call sites might
+    //  already be providing a value for subsequent parameters. Unfortunately we
+    //  can't know at this point whether there are subsequent parameters in
+    //  order to require it only when it's potentially necessary.
+    if (isRequired && argumentValue == null) {
+      // TODO(brianwilkerson) Report that required parameters must have an
+      //  argument value.
+      return;
+    }
+    _parameterModifications ??= [];
+    _parameterModifications.add(AddParameter(
+        index, name, isRequired, isPositional, defaultValue, argumentValue));
+  }
+
   /// Translate the [node] into an add-type-parameter change. Return the
   /// resulting change, or `null` if the [node] does not represent a valid
   /// add-type-parameter change.
@@ -219,6 +275,12 @@
         return _translateAddTypeParameterChange(node);
       } else if (kind == _renameKind) {
         return _translateRenameChange(node);
+      } else if (kind == _addParameterKind) {
+        _translateAddParameterChange(node);
+        return null;
+      } else if (kind == _removeParameterKind) {
+        _translateRemoveParameterChange(node);
+        return null;
       }
       // TODO(brianwilkerson) Report the invalid change kind.
       return null;
@@ -248,7 +310,7 @@
       }
       var elementKey = _singleKey(node, [
         _classKey,
-        _enumConstantKey,
+        _constantKey,
         _constructorKey,
         _enumKey,
         _extensionKey,
@@ -270,7 +332,7 @@
       var containerName =
           _translateString(node.valueAt(containerKey), containerKey);
       if (containerName == null) {
-        if ([_constructorKey, _enumConstantKey, _methodKey, _fieldKey]
+        if ([_constructorKey, _constantKey, _methodKey, _fieldKey]
             .contains(elementKey)) {
           // TODO(brianwilkerson) Report that no container was found.
           return null;
@@ -338,6 +400,25 @@
     }
   }
 
+  /// Translate the [node] into a remove-parameter modification.
+  void _translateRemoveParameterChange(YamlMap node) {
+    _singleKey(node, [_indexKey, _nameKey]);
+    _reportUnsupportedKeys(node, const {_indexKey, _kindKey, _nameKey});
+    ParameterReference reference;
+    var index = _translateInteger(node.valueAt(_indexKey), _indexKey);
+    if (index != null) {
+      reference = PositionalParameterReference(index);
+    } else {
+      var name = _translateString(node.valueAt(_nameKey), _nameKey);
+      if (name == null) {
+        return;
+      }
+      reference = NamedParameterReference(name);
+    }
+    _parameterModifications ??= [];
+    _parameterModifications.add(RemoveParameter(reference));
+  }
+
   /// Translate the [node] into a rename change. Return the resulting change, or
   /// `null` if the [node] does not represent a valid rename change.
   Rename _translateRenameChange(YamlMap node) {
@@ -380,12 +461,16 @@
       _reportUnsupportedKeys(node, const {_changesKey, _elementKey, _titleKey});
       var title = _translateString(node.valueAt(_titleKey), _titleKey);
       var element = _translateElement(node.valueAt(_elementKey), _elementKey);
-      var changes = _translateList<Change>(
+      var changes = _translateList(
           node.valueAt(_changesKey), _changesKey, _translateChange);
       if (changes == null) {
         // The error has already been reported.
         return null;
       }
+      if (_parameterModifications != null) {
+        changes.add(ModifyParameters(modifications: _parameterModifications));
+        _parameterModifications = null;
+      }
       return Transform(title: title, element: element, changes: changes);
     } else if (node == null) {
       // TODO(brianwilkerson) Report the missing YAML.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
index ad5243b..cc9b79f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/value_extractor.dart
@@ -19,14 +19,27 @@
 
   @override
   String from(AstNode node, CorrectionUtils utils) {
-    if (node is InvocationExpression) {
-      var expression = parameter.argumentFrom(node.argumentList);
+    var argumentList = _getArgumentList(node);
+    if (argumentList != null) {
+      var expression = parameter.argumentFrom(argumentList);
       if (expression != null) {
         return utils.getNodeText(expression);
       }
     }
     return null;
   }
+
+  /// Return the argument list associated with the given [node].
+  ArgumentList _getArgumentList(AstNode node) {
+    if (node is ArgumentList) {
+      return node;
+    } else if (node is InvocationExpression) {
+      return node.argumentList;
+    } else if (node is InstanceCreationExpression) {
+      return node.argumentList;
+    }
+    return null;
+  }
 }
 
 /// A value extractor that returns a pre-computed piece of code.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 4740406..a979965 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -504,9 +504,11 @@
     ],
     CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS: [
       AddMissingParameter.newInstance,
+      DataDriven.newInstance,
     ],
     CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED: [
       AddMissingParameter.newInstance,
+      DataDriven.newInstance,
     ],
     CompileTimeErrorCode.IMPLEMENTS_NON_CLASS: [
       DataDriven.newInstance,
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/make_final_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/make_final_test.dart
index d31d543..cae164a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/make_final_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/make_final_test.dart
@@ -10,6 +10,7 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(PreferFinalFieldsTest);
+    defineReflectiveTests(PreferFinalLocalsTest);
   });
 }
 
@@ -37,3 +38,24 @@
 ''');
   }
 }
+
+@reflectiveTest
+class PreferFinalLocalsTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.prefer_final_locals;
+
+  Future<void> test_singleFile() async {
+    await resolveTestUnit('''
+f() {
+  var x = 0;
+  var y = x;
+}
+''');
+    await assertHasFix('''
+f() {
+  final x = 0;
+  final y = x;
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
index 1f0eb58..b5ae21c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
@@ -14,6 +14,45 @@
 
 @reflectiveTest
 class EndToEndTest extends DataDrivenFixProcessorTest {
+  Future<void> test_addParameter() async {
+    setPackageContent('''
+class C {
+  void m(int x, int y) {}
+}
+''');
+    addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Add parameter'
+  element:
+    uris: ['$importUri']
+    method: 'm'
+    inClass: 'C'
+  changes:
+    - kind: 'addParameter'
+      index: 1
+      name: 'y'
+      style: required_positional
+      argumentValue:
+        kind: 'argument'
+        index: 0
+''');
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m(0);
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m(0, 0);
+}
+''');
+  }
+
   Future<void> test_addTypeParameter() async {
     setPackageContent('''
 class C {
@@ -53,6 +92,40 @@
 ''');
   }
 
+  Future<void> test_removeParameter() async {
+    setPackageContent('''
+class C {
+  void m(int x) {}
+}
+''');
+    addPackageDataFile('''
+version: 1
+transforms:
+- title: 'Add argument'
+  element:
+    uris: ['$importUri']
+    method: 'm'
+    inClass: 'C'
+  changes:
+    - kind: 'removeParameter'
+      index: 1
+''');
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.m(0, 1);
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.m(0);
+}
+''');
+  }
+
   Future<void> test_rename() async {
     setPackageContent('''
 class New {}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index 58dc538..e5e8e1f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/modify_parameters.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
@@ -21,6 +22,146 @@
 
 @reflectiveTest
 class TransformSetParserTest extends AbstractTransformSetParserTest {
+  void test_addParameter_optionalNamed() {
+    parse('''
+version: 1
+transforms:
+- title: 'Add'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'addParameter'
+      index: 0
+      name: 'p'
+      style: optional_named
+      argumentValue:
+        kind: 'argument'
+        index: 1
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Add');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as AddParameter;
+    expect(modification.index, 0);
+    expect(modification.name, 'p');
+    expect(modification.isRequired, false);
+    expect(modification.isPositional, false);
+    var value = modification.argumentValue as ArgumentExtractor;
+    var parameter = value.parameter as PositionalParameterReference;
+    expect(parameter.index, 1);
+  }
+
+  void test_addParameter_optionalPositional() {
+    parse('''
+version: 1
+transforms:
+- title: 'Add'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'addParameter'
+      index: 0
+      name: 'p'
+      style: optional_positional
+      defaultValue:
+        kind: 'argument'
+        index: 1
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Add');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as AddParameter;
+    expect(modification.index, 0);
+    expect(modification.name, 'p');
+    expect(modification.isRequired, false);
+    expect(modification.isPositional, true);
+    var value = modification.defaultValue as ArgumentExtractor;
+    var parameter = value.parameter as PositionalParameterReference;
+    expect(parameter.index, 1);
+  }
+
+  void test_addParameter_requiredNamed() {
+    parse('''
+version: 1
+transforms:
+- title: 'Add'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'addParameter'
+      index: 0
+      name: 'p'
+      style: required_named
+      argumentValue:
+        kind: 'argument'
+        index: 1
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Add');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as AddParameter;
+    expect(modification.index, 0);
+    expect(modification.name, 'p');
+    expect(modification.isRequired, true);
+    expect(modification.isPositional, false);
+    var value = modification.argumentValue as ArgumentExtractor;
+    var parameter = value.parameter as PositionalParameterReference;
+    expect(parameter.index, 1);
+  }
+
+  void test_addParameter_requiredPositional() {
+    parse('''
+version: 1
+transforms:
+- title: 'Add'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'addParameter'
+      index: 0
+      name: 'p'
+      style: required_positional
+      argumentValue:
+        kind: 'argument'
+        index: 1
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Add');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as AddParameter;
+    expect(modification.index, 0);
+    expect(modification.name, 'p');
+    expect(modification.isRequired, true);
+    expect(modification.isPositional, true);
+    var value = modification.argumentValue as ArgumentExtractor;
+    var parameter = value.parameter as PositionalParameterReference;
+    expect(parameter.index, 1);
+  }
+
   void test_addTypeParameter_fromNamedArgument() {
     parse('''
 version: 1
@@ -155,6 +296,56 @@
     ]);
   }
 
+  void test_removeParameter_named() {
+    parse('''
+version: 1
+transforms:
+- title: 'Remove'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'removeParameter'
+      name: 'p'
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Remove');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as RemoveParameter;
+    var parameter = modification.parameter as NamedParameterReference;
+    expect(parameter.name, 'p');
+  }
+
+  void test_removeParameter_positional() {
+    parse('''
+version: 1
+transforms:
+- title: 'Remove'
+  element:
+    uris: ['test.dart']
+    function: 'f'
+  changes:
+    - kind: 'removeParameter'
+      index: 0
+''');
+    var transforms = result.transformsFor('f', ['test.dart']);
+    expect(transforms, hasLength(1));
+    var transform = transforms[0];
+    expect(transform.title, 'Remove');
+    expect(transform.changes, hasLength(1));
+    var change = transform.changes[0] as ModifyParameters;
+    var modifications = change.modifications;
+    expect(modifications, hasLength(1));
+    var modification = modifications[0] as RemoveParameter;
+    var parameter = modification.parameter as PositionalParameterReference;
+    expect(parameter.index, 0);
+  }
+
   void test_rename() {
     parse('''
 version: 1
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index abd886f..0d888f2 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -576,149 +576,255 @@
   }
 
   static Future<ConnectionTask<_NativeSocket>> startConnect(
-      dynamic host, int port, dynamic sourceAddress) {
+      dynamic host, int port, dynamic sourceAddress) async {
     if (host is String) {
       host = escapeLinkLocalAddress(host);
     }
     _throwOnBadPort(port);
-    if (sourceAddress != null && sourceAddress is! _InternetAddress) {
-      if (sourceAddress is String) {
-        sourceAddress = new InternetAddress(sourceAddress);
+    if (sourceAddress is String) {
+      sourceAddress = InternetAddress(sourceAddress);
+    } else if (sourceAddress != null && sourceAddress is! _InternetAddress) {
+      throw ArgumentError(
+          'sourceAddress $sourceAddress must be either String or InternetAddress');
+    }
+
+    // The stream containing Internet addresses
+    final stream = await _lookup(host);
+    var streamSubscription;
+    final completer = Completer<_NativeSocket>();
+    // The first error that occurs.
+    var error = null;
+    // The map contains currently attempted connections.
+    final connecting = HashMap<_NativeSocket, Timer>();
+    // A queue contains Internet addresses that waits for connecting.
+    // `stream` keeps pushing element into queue, and connectNext() consume
+    // them.
+    final queue = <InternetAddress>[];
+    // The flag that stream of addresses is done.
+    var streamClosed = false;
+    // connectNext() will exhaust all elements from the queue. In the case of an
+    // element taking so long that connectNext() has finished trying all
+    // addresses, an explicit call to connectNext() is needed to restart the
+    // process.
+    var tryConnect = true;
+
+    // It will try each element in the `queue` to establish the connection.
+    // There can be multiple Internet addresses are under processing. Whenever
+    // a connection is successfully established (by receiving a write event),
+    // all pending connections stored in the `connecting` will be cancelled.
+    //
+    // 1. Check the status of `queue`, `completer` and `connecting`.
+    //      Return an error if all elements have been tried.
+    //      Reset tryConnect so that next element will be examined if `stream`
+    //      is still open.
+    // 2. Create a socket.
+    //      Try next address by issuing another connectNext() if this failed and
+    //      keep the first error.
+    // 3. Register a timer, if socket creation succeeds, to try next address.
+    // 4. Set up a handler for socket. It will cancel all other pending
+    //    connections and timers if it succeeds. Otherwise, keep the first
+    //    error and issue connectNext().
+    void connectNext() {
+      if (completer.isCompleted) return;
+      if (queue.isEmpty) {
+        if (streamClosed && connecting.isEmpty) {
+          assert(error != null);
+          completer.completeError(error);
+        } else if (!streamClosed) {
+          // If new addresses comes after all elements in the queue have been
+          // processed, issue another connectNext().
+          tryConnect = true;
+        }
+        return;
+      }
+      final _InternetAddress address = queue.removeAt(0) as _InternetAddress;
+      var socket = _NativeSocket.normal(address);
+      var result;
+      if (sourceAddress == null) {
+        if (address.type == InternetAddressType.unix) {
+          result = socket.nativeCreateUnixDomainConnect(
+              address.address, _Namespace._namespace);
+        } else {
+          result = socket.nativeCreateConnect(
+              address._in_addr, port, address._scope_id);
+        }
+      } else {
+        assert(sourceAddress is _InternetAddress);
+        if (address.type == InternetAddressType.unix) {
+          assert(sourceAddress.type == InternetAddressType.unix);
+          result = socket.nativeCreateUnixDomainBindConnect(
+              address.address, sourceAddress.address, _Namespace._namespace);
+        } else {
+          result = socket.nativeCreateBindConnect(address._in_addr, port,
+              sourceAddress._in_addr, address._scope_id);
+        }
+      }
+      if (result is OSError) {
+        // Keep first error, if present.
+        if (error == null) {
+          int errorCode = result.errorCode;
+          if (sourceAddress != null &&
+              errorCode != null &&
+              socket.isBindError(errorCode)) {
+            error = createError(result, "Bind failed", sourceAddress);
+          } else {
+            error = createError(result, "Connection failed", address, port);
+          }
+        }
+        connectNext();
+      } else {
+        // Query the local port for error messages.
+        try {
+          socket.port;
+        } catch (e) {
+          error ??= createError(e, "Connection failed", address, port);
+          connectNext();
+          return;
+        }
+        // Set up timer for when we should retry the next address
+        // (if any).
+        final duration =
+            address.isLoopback ? _retryDurationLoopback : _retryDuration;
+        final timer = Timer(duration, connectNext);
+        setupResourceInfo(socket);
+
+        connecting[socket] = timer;
+        // Setup handlers for receiving the first write event which
+        // indicate that the socket is fully connected.
+        socket.setHandlers(write: () {
+          timer.cancel();
+          connecting.remove(socket);
+          // From 'man 2 connect':
+          // After select(2) indicates writability, use getsockopt(2) to read
+          // the SO_ERROR option at level SOL_SOCKET to determine whether
+          // connect() completed successfully (SO_ERROR is zero) or
+          // unsuccessfully.
+          OSError osError = socket.nativeGetError();
+          if (osError.errorCode != 0) {
+            socket.close();
+            error ??= osError;
+            // No timer is active for triggering next tryConnect(), do it
+            // manually.
+            if (connecting.isEmpty) connectNext();
+            return;
+          }
+          socket.setListening(read: false, write: false);
+          completer.complete(socket);
+          connecting.forEach((s, t) {
+            t.cancel();
+            s.close();
+            s.setHandlers();
+            s.setListening(read: false, write: false);
+          });
+          connecting.clear();
+        }, error: (e, st) {
+          timer.cancel();
+          socket.close();
+          // Keep first error, if present.
+          error ??= e;
+          connecting.remove(socket);
+          if (connecting.isEmpty) connectNext();
+        });
+        socket.setListening(read: false, write: true);
       }
     }
-    return new Future.value(host).then((host) {
-      if (host is _InternetAddress) return [host];
-      return lookup(host).then((addresses) {
+
+    void onCancel() {
+      connecting.forEach((s, t) {
+        t.cancel();
+        s.close();
+        s.setHandlers();
+        s.setListening(read: false, write: false);
+        if (error == null) {
+          error = createError(null,
+              "Connection attempt cancelled, host: ${host}, port: ${port}");
+        }
+      });
+      connecting.clear();
+      if (!completer.isCompleted) {
+        completer.completeError(error! as Object);
+      }
+    }
+
+    // The stream is constructed in the _lookup() and should not emit the error.
+    streamSubscription = stream.listen((address) {
+      queue.add(address);
+      if (tryConnect) {
+        tryConnect = false;
+        connectNext();
+      }
+    }, onError: (e) {
+      streamSubscription.cancel();
+      streamClosed = true;
+      // The error comes from lookup() and we just rethrow the error.
+      throw e;
+    }, onDone: () {
+      streamClosed = true;
+      // In case that stream closes later than connectNext() exhausts all
+      // Internet addresses, check whether an error is thrown.
+      if (queue.isEmpty && connecting.isEmpty && !completer.isCompleted) {
+        completer.completeError(error);
+      }
+    });
+
+    return Future.value(
+        ConnectionTask<_NativeSocket>._(completer.future, onCancel));
+  }
+
+  // The [host] should be either a String or an _InternetAddress.
+  static Future<Stream> _lookup(host) async {
+    if (host is _InternetAddress) {
+      return Stream.value(host);
+    } else {
+      assert(host is String);
+      // If host is an IPv4 or IPv6 literal, bypass the lookup.
+      final inAddr = _InternetAddress._parse(host);
+      if (inAddr != null && inAddr.length == _InternetAddress._IPv4AddrLength) {
+        var addresses = await lookup(host, type: InternetAddressType.IPv4);
         if (addresses.isEmpty) {
           throw createError(null, "Failed host lookup: '$host'");
         }
-        return addresses;
-      });
-    }).then((addresses) {
-      var completer = new Completer<_NativeSocket>();
-      var it = (addresses as List<InternetAddress>).iterator;
-      var error = null;
-      var connecting = new HashMap();
-
-      void connectNext() {
-        if (!it.moveNext()) {
-          if (connecting.isEmpty) {
-            assert(error != null);
-            completer.completeError(error);
-          }
-          return;
+        return Stream.fromIterable(addresses);
+      }
+      if (inAddr != null && inAddr.length == _InternetAddress._IPv6AddrLength) {
+        var addresses = await lookup(host, type: InternetAddressType.IPv6);
+        if (addresses.isEmpty) {
+          throw createError(null, "Failed host lookup: '$host'");
         }
-        final _InternetAddress address = it.current as _InternetAddress;
-        var socket = new _NativeSocket.normal(address);
-        var result;
-        if (sourceAddress == null) {
-          if (address.type == InternetAddressType.unix) {
-            result = socket.nativeCreateUnixDomainConnect(
-                address.address, _Namespace._namespace);
-          } else {
-            result = socket.nativeCreateConnect(
-                address._in_addr, port, address._scope_id);
-          }
-        } else {
-          assert(sourceAddress is _InternetAddress);
-          if (address.type == InternetAddressType.unix) {
-            assert(sourceAddress.type == InternetAddressType.unix);
-            result = socket.nativeCreateUnixDomainBindConnect(
-                address.address, sourceAddress.address, _Namespace._namespace);
-          } else {
-            result = socket.nativeCreateBindConnect(address._in_addr, port,
-                sourceAddress._in_addr, address._scope_id);
-          }
-        }
-        if (result is OSError) {
-          // Keep first error, if present.
-          if (error == null) {
-            int errorCode = result.errorCode;
-            if (sourceAddress != null &&
-                errorCode != null &&
-                socket.isBindError(errorCode)) {
-              error = createError(result, "Bind failed", sourceAddress);
-            } else {
-              error = createError(result, "Connection failed", address, port);
-            }
-          }
-          connectNext();
-        } else {
-          // Query the local port for error messages.
-          try {
-            socket.port;
-          } catch (e) {
-            if (error == null) {
-              error = createError(e, "Connection failed", address, port);
-            }
-            connectNext();
-          }
-          // Set up timer for when we should retry the next address
-          // (if any).
-          var duration =
-              address.isLoopback ? _retryDurationLoopback : _retryDuration;
-          var timer = new Timer(duration, connectNext);
-          setupResourceInfo(socket);
-
-          connecting[socket] = timer;
-          // Setup handlers for receiving the first write event which
-          // indicate that the socket is fully connected.
-          socket.setHandlers(write: () {
-            timer.cancel();
-            connecting.remove(socket);
-            // From 'man 2 connect':
-            // After select(2) indicates writability, use getsockopt(2) to read
-            // the SO_ERROR option at level SOL_SOCKET to determine whether
-            // connect() completed successfully (SO_ERROR is zero) or
-            // unsuccessfully.
-            OSError osError = socket.nativeGetError();
-            if (osError.errorCode != 0) {
-              socket.close();
-              if (error == null) error = osError;
-              if (connecting.isEmpty) connectNext();
-              return;
-            }
-            socket.setListening(read: false, write: false);
-            completer.complete(socket);
-            connecting.forEach((s, t) {
-              t.cancel();
-              s.close();
-              s.setHandlers();
-              s.setListening(read: false, write: false);
-            });
-            connecting.clear();
-          }, error: (e, st) {
-            timer.cancel();
-            socket.close();
-            // Keep first error, if present.
-            if (error == null) error = e;
-            connecting.remove(socket);
-            if (connecting.isEmpty) connectNext();
-          });
-          socket.setListening(read: false, write: true);
-        }
+        return Stream.fromIterable(addresses);
       }
 
-      void onCancel() {
-        connecting.forEach((s, t) {
-          t.cancel();
-          s.close();
-          s.setHandlers();
-          s.setListening(read: false, write: false);
-          if (error == null) {
-            error = createError(null,
-                "Connection attempt cancelled, host: ${host}, port: ${port}");
-          }
-        });
-        connecting.clear();
-        if (!completer.isCompleted) {
-          completer.completeError(error);
+      if (Platform.isIOS) {
+        return _concurrentLookup(host);
+      } else {
+        var addresses = await lookup(host, type: InternetAddressType.any);
+        if (addresses.isEmpty) {
+          throw createError(null, "Failed host lookup: '$host'");
         }
+        return Stream.fromIterable(addresses);
       }
+    }
+  }
 
-      connectNext();
-      return new ConnectionTask<_NativeSocket>._(completer.future, onCancel);
+  static Stream<InternetAddress> _concurrentLookup(String host) {
+    final controller = StreamController<InternetAddress>();
+    // Lookup IPv4 and IPv6 concurrently
+    Future.wait([
+      for (final type in [InternetAddressType.IPv4, InternetAddressType.IPv6])
+        lookup(host, type: type).then((list) {
+          for (final address in list) {
+            controller.add(address);
+          }
+          return list.isNotEmpty;
+        })
+    ]).then((successes) {
+      if (!successes.contains(true)) {
+        // Neither lookup found an address.
+        throw createError(null, "Failed host lookup: '$host'");
+      }
+      controller.close();
     });
+    return controller.stream;
   }
 
   static Future<_NativeSocket> connect(
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index d2a0feb..48f428f 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -203,7 +203,7 @@
    * change over time.
    */
   external static Future<List<InternetAddress>> lookup(String host,
-      {InternetAddressType type: InternetAddressType.any});
+      {InternetAddressType type = InternetAddressType.any});
 
   /**
    * Clones the given [address] with the new [host].
@@ -264,9 +264,9 @@
    * specified type. Default is [InternetAddressType.any].
    */
   external static Future<List<NetworkInterface>> list(
-      {bool includeLoopback: false,
-      bool includeLinkLocal: false,
-      InternetAddressType type: InternetAddressType.any});
+      {bool includeLoopback = false,
+      bool includeLinkLocal = false,
+      InternetAddressType type = InternetAddressType.any});
 }
 
 /**
@@ -315,7 +315,7 @@
    * distributed over multiple isolates this way.
    */
   external static Future<RawServerSocket> bind(address, int port,
-      {int backlog: 0, bool v6Only: false, bool shared: false});
+      {int backlog = 0, bool v6Only = false, bool shared = false});
 
   /**
    * Returns the port used by this socket.
@@ -391,7 +391,8 @@
   }
 
   external static Future<ServerSocket> _bind(address, int port,
-      {int backlog: 0, bool v6Only: false, bool shared: false});
+      {int backlog = 0, bool v6Only = false, bool shared = false});
+
   /**
    * Returns the port used by this socket.
    */
@@ -558,11 +559,21 @@
 
 /**
  * Events for the [RawSocket].
+ *
+ * These event objects are by the [Stream] behavior of [RawSocket] (for example
+ * [RawSocket.listen], [RawSocket.forEach]) when the socket's state change.
  */
 class RawSocketEvent {
+  /// An event indicates the socket is ready to be read.
   static const RawSocketEvent read = const RawSocketEvent._(0);
+
+  /// An event indicates the socket is ready to write.
   static const RawSocketEvent write = const RawSocketEvent._(1);
+
+  /// An event indicates the reading from the socket is closed
   static const RawSocketEvent readClosed = const RawSocketEvent._(2);
+
+  /// An event indicates the socket is closed.
   static const RawSocketEvent closed = const RawSocketEvent._(3);
 
   @Deprecated("Use read instead")
@@ -610,15 +621,17 @@
   }
 }
 
-/**
- * A [RawSocket] is an unbuffered interface to a TCP socket.
- *
- * The raw socket delivers the data stream in the same chunks as the underlying
- * operating system.
- *
- * It is not the same as a
- * [POSIX raw socket](http://man7.org/linux/man-pages/man7/raw.7.html).
- */
+/// A TCP connection.
+///
+/// A *socket connection* connects a *local* socket to a *remote* socket.
+/// Data, as [Uint8List]s, is received by the local socket and made
+/// available by the [read] method, and can be sent to the remote socket
+/// through the [write] method.
+///
+/// The [Stream] interface of this class provides event notification about when
+/// a certain change has happened, for example when data has become available
+/// ([RawSocketEvent.read]) or when the remote end has stopped listening
+/// ([RawSocketEvent.close]).
 abstract class RawSocket implements Stream<RawSocketEvent> {
   /**
    * Set or get, if the [RawSocket] should listen for [RawSocketEvent.read]
@@ -768,12 +781,12 @@
   void setRawOption(RawSocketOption option);
 }
 
-/**
- * A high-level class for communicating over a TCP socket.
- *
- * The [Socket] exposes both a [Stream] and a [IOSink] interface, making it
- * ideal for using together with other [Stream]s.
- */
+/// A TCP connection between two sockets.
+///
+/// A *socket connection* connects a *local* socket to a *remote* socket.
+/// Data, as [Uint8List]s, is received by the local socket, made available
+/// by the [Stream] interface of this class, and can be sent to the remote
+/// socket through the [IOSink] interface of this class.
 abstract class Socket implements Stream<Uint8List>, IOSink {
   /**
    * Creates a new socket connection to the host and port and returns a [Future]
@@ -903,14 +916,15 @@
   Future get done;
 }
 
-/**
- * Datagram package. Data sent to and received from datagram sockets
- * contains the internet address and port of the destination or source
- * togeter with the data.
- */
+/// A data packet which is received by a [RawDatagramSocket].
 class Datagram {
+  /// The actual bytes of the message.
   Uint8List data;
+
+  /// The address of the socket which sends the data.
   InternetAddress address;
+
+  /// The port of the socket which sends the data.
   int port;
 
   Datagram(this.data, this.address, this.port);
@@ -990,11 +1004,29 @@
   void set broadcastEnabled(bool value);
 
   /**
-   * Creates a new raw datagram socket binding it to an address and
-   * port.
+   * Binds a socket to the given [host] and the [port].
+   *
+   * When the socket is bound and has started listening on [port], the returned
+   * future completes with the [RawDatagramSocket] of the bound socket.
+   *
+   * The [host] can either be a [String] or an [InternetAddress]. If [host] is a
+   * [String], [bind] will perform a [InternetAddress.lookup] and use the first
+   * value in the list. To listen on the loopback interface, which will allow
+   * only incoming connections from the local host, use the value
+   * [InternetAddress.loopbackIPv4] or [InternetAddress.loopbackIPv6].
+   * To allow for incoming connection from any network use either one of
+   * the values [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
+   * bind to all interfaces, or use the IP address of a specific interface.
+   *
+   * The [reuseAddress] should be set for all listeners that bind to the same
+   * address. Otherwise, it will fail with a [SocketException].
+   *
+   * The [reusePort] specifies whether the port can be reused.
+   *
+   * The [ttl] sets `time to live` of a datagram sent on the socket.
    */
   external static Future<RawDatagramSocket> bind(host, int port,
-      {bool reuseAddress: true, bool reusePort: false, int ttl: 1});
+      {bool reuseAddress = true, bool reusePort = false, int ttl = 1});
 
   /**
    * Returns the port used by this socket.
@@ -1062,13 +1094,34 @@
   void setRawOption(RawSocketOption option);
 }
 
+/// Exception thrown when a socket operation fails.
 class SocketException implements IOException {
+  /// Description of the error.
   final String message;
+
+  /// The underlying OS error.
+  ///
+  /// If this exception is not thrown due to an OS error, the value is `null`.
   final OSError? osError;
+
+  /// The address of the socket giving rise to the exception.
+  ///
+  /// This is either the source or destination address of a socket,
+  /// or it can be `null` if no socket end-point was involved in the cause of
+  /// the exception.
   final InternetAddress? address;
+
+  /// The port of the socket giving rise to the exception.
+  ///
+  /// This is either the source or destination address of a socket,
+  /// or it can be `null` if no socket end-point was involved in the cause of
+  /// the exception.
   final int? port;
 
+  /// Creates a [SocketException] with the provided values.
   const SocketException(this.message, {this.osError, this.address, this.port});
+
+  /// Creates an exception reporting that a socket was used after it was closed.
   const SocketException.closed()
       : message = 'Socket has been closed',
         osError = null,
diff --git a/tools/VERSION b/tools/VERSION
index 7aea38a..13e4a72 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 109
+PRERELEASE 110
 PRERELEASE_PATCH 0
\ No newline at end of file