Version 2.15.0-112.0.dev

Merge commit '9ecb2d0616d55b57730a5f9784eb6bb4627382eb' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/make_return_type_nullable.dart b/pkg/analysis_server/lib/src/services/correction/dart/make_return_type_nullable.dart
index 2f2107d..146e42b 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/make_return_type_nullable.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/make_return_type_nullable.dart
@@ -25,6 +25,11 @@
       return;
     }
 
+    final type = node.staticType;
+    if (type == null) {
+      return;
+    }
+
     var body = node.thisOrAncestorOfType<FunctionBody>();
     if (body == null) {
       return;
@@ -37,22 +42,22 @@
 
     if (body.isAsynchronous || body.isGenerator) {
       if (returnType is! NamedType) {
-        return null;
+        return;
       }
       var typeArguments = returnType.typeArguments;
       if (typeArguments == null) {
-        return null;
+        return;
       }
       var arguments = typeArguments.arguments;
       if (arguments.length != 1) {
-        return null;
+        return;
       }
       returnType = arguments[0];
     }
 
     if (node is! NullLiteral &&
-        !typeSystem.isAssignableTo(returnType.typeOrThrow,
-            typeSystem.promoteToNonNull(node.typeOrThrow))) {
+        !typeSystem.isAssignableTo(
+            returnType.typeOrThrow, typeSystem.promoteToNonNull(type))) {
       return;
     }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/make_return_type_nullable_test.dart b/pkg/analysis_server/test/src/services/correction/fix/make_return_type_nullable_test.dart
index 1b3a4cc..ae6ff05 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/make_return_type_nullable_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/make_return_type_nullable_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.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -86,6 +87,23 @@
 ''');
   }
 
+  /// This code is parsed in such a way that we find `void` as an `Expression`.
+  /// But this expression is a name in a `NamedType`, and so `NamedType` has
+  /// the `type`, but not `void` - its type is `null`. So, the producer should
+  /// check for `null`, and don't expect that every expression has a type.
+  Future<void> test_getter_sync_invalidVoid() async {
+    await resolveTestCode('''
+int f() {
+  return void;
+}
+''');
+    await assertNoFix(
+      errorFilter: (e) =>
+          e.errorCode ==
+          CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION,
+    );
+  }
+
   Future<void> test_incompatibilityIsNotLimitedToNullability() async {
     await resolveTestCode('''
 int f() {
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index bc6446c..67f2db7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -171,15 +171,6 @@
   final _indexRequestedFiles =
       <String, List<Completer<AnalysisDriverUnitIndex>>>{};
 
-  /// The mapping from the files for which the unit element key was requested
-  /// using `getUnitElementSignature` to the [Completer]s to report the result.
-  final _unitElementSignatureFiles = <String, List<Completer<String>>>{};
-
-  /// The mapping from the files for which the unit element key was requested
-  /// using `getUnitElementSignature`, and which were found to be parts without
-  /// known libraries, to the [Completer]s to report the result.
-  final _unitElementSignatureParts = <String, List<Completer<String>>>{};
-
   /// The mapping from the files for which the unit element was requested using
   /// [getUnitElement2] to the [Completer]s to report the result.
   final _unitElementRequestedFiles =
@@ -434,9 +425,6 @@
     if (_indexRequestedFiles.isNotEmpty) {
       return AnalysisDriverPriority.interactive;
     }
-    if (_unitElementSignatureFiles.isNotEmpty) {
-      return AnalysisDriverPriority.interactive;
-    }
     if (_unitElementRequestedFiles.isNotEmpty) {
       return AnalysisDriverPriority.interactive;
     }
@@ -465,7 +453,6 @@
     if (_errorsRequestedParts.isNotEmpty ||
         _requestedParts.isNotEmpty ||
         _partsToAnalyze.isNotEmpty ||
-        _unitElementSignatureParts.isNotEmpty ||
         _unitElementRequestedParts.isNotEmpty) {
       return AnalysisDriverPriority.general;
     }
@@ -837,17 +824,6 @@
     );
   }
 
-  ApiSignature getResolvedUnitKeyByPath(String path) {
-    _throwIfNotAbsolutePath(path);
-    var file = fsState.getFileForPath(path);
-
-    var signature = ApiSignature();
-    signature.addUint32List(_saltForResolution);
-    signature.addString(file.transitiveSignature);
-    signature.addString(file.contentHash);
-    return signature;
-  }
-
   /// Return a [Future] that completes with a [SomeResolvedUnitResult] for the
   /// Dart file with the given [path].  If the file cannot be analyzed,
   /// the [Future] completes with an [InvalidResult].
@@ -1057,23 +1033,6 @@
       return;
     }
 
-    // Process a unit element key request.
-    if (_unitElementSignatureFiles.isNotEmpty) {
-      String path = _unitElementSignatureFiles.keys.first;
-      String? signature = _computeUnitElementSignature(path);
-      var completers = _unitElementSignatureFiles.remove(path)!;
-      if (signature != null) {
-        completers.forEach((completer) {
-          completer.complete(signature);
-        });
-      } else {
-        _unitElementSignatureParts
-            .putIfAbsent(path, () => [])
-            .addAll(completers);
-      }
-      return;
-    }
-
     // Process a unit element request.
     if (_unitElementRequestedFiles.isNotEmpty) {
       String path = _unitElementRequestedFiles.keys.first;
@@ -1211,17 +1170,6 @@
       return;
     }
 
-    // Process a unit element signature request for a part.
-    if (_unitElementSignatureParts.isNotEmpty) {
-      String path = _unitElementSignatureParts.keys.first;
-      String signature =
-          _computeUnitElementSignature(path, asIsIfPartWithoutLibrary: true)!;
-      _unitElementSignatureParts.remove(path)!.forEach((completer) {
-        completer.complete(signature);
-      });
-      return;
-    }
-
     // Process a unit element request for a part.
     if (_unitElementRequestedParts.isNotEmpty) {
       String path = _unitElementRequestedParts.keys.first;
@@ -1508,23 +1456,6 @@
     });
   }
 
-  String? _computeUnitElementSignature(String path,
-      {bool asIsIfPartWithoutLibrary = false}) {
-    FileState file = _fsState.getFileForPath(path);
-
-    // Prepare the library - the file itself, or the known library.
-    FileState? library = file.isPart ? file.library : file;
-    if (library == null) {
-      if (asIsIfPartWithoutLibrary) {
-        library = file;
-      } else {
-        return null;
-      }
-    }
-
-    return library.transitiveSignature;
-  }
-
   /// Creates new [FileSystemState] and [FileTracker] objects.
   ///
   /// This is used both on initial construction and whenever the configuration
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 66bc7a5..3132dfd 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -141,7 +141,6 @@
 
   LibraryCycle? _libraryCycle;
   String? _transitiveSignature;
-  String? _transitiveSignatureLinked;
 
   /// The flag that shows whether the file has an error or warning that
   /// might be fixed by a change to another file.
@@ -350,11 +349,6 @@
     return _transitiveSignature!;
   }
 
-  /// The value `transitiveSignature.linked` is used often, so we cache it.
-  String get transitiveSignatureLinked {
-    return _transitiveSignatureLinked ??= '$transitiveSignature.linked';
-  }
-
   /// The [UnlinkedUnit2] of the file.
   UnlinkedUnit2 get unlinked2 => _unlinked2!;
 
@@ -390,7 +384,6 @@
     if (cycle == null) {
       _libraryCycle = null;
       _transitiveSignature = null;
-      _transitiveSignatureLinked = null;
     } else {
       _libraryCycle = cycle;
       _transitiveSignature = signature;
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index 413d6ab..2d5b660 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -36,20 +36,6 @@
   /// files that [libraries] reference (but we don't compute these files).
   String? transitiveSignature;
 
-  /// The map from a library in [libraries] to its transitive signature.
-  ///
-  /// It is almost the same as [transitiveSignature], but is also based on
-  /// the URI of this specific library.  Currently we store each linked library
-  /// with its own key, so we need unique keys.  However practically we never
-  /// can use just *one* library of a cycle, we always use the whole cycle.
-  ///
-  /// TODO(scheglov) Switch to loading the whole cycle maybe?
-  final Map<FileState, String> transitiveSignatures = {};
-
-  LibraryCycle();
-
-  LibraryCycle.external() : transitiveSignature = '<external>';
-
   /// Invalidate this cycle and any cycles that directly or indirectly use it.
   ///
   /// Practically invalidation means that we clear the library cycle in all the
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index d1755aa..87d4094 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1409,7 +1409,8 @@
 
       if (targetChecks.checkAllParameters ||
           (targetChecks.checkCovariantParameters &&
-              (variable.isCovariantByClass || variable.isCovariantByDeclaration))) {
+              (variable.isCovariantByClass ||
+                  variable.isCovariantByDeclaration))) {
         newParameter = _typeBuilder.potentiallyCheckOrTrustTypeOfParameter(
             targetElement, newParameter, type);
       } else {
@@ -3747,7 +3748,7 @@
           // Filter elided parameters.
           .where((p) => parameterStructure.namedParameters.contains(p.name))
           .toList()
-            ..sort(namedOrdering);
+        ..sort(namedOrdering);
       for (ir.VariableDeclaration parameter in namedParameters) {
         HInstruction value = namedValues[parameter.name];
         if (value == null) {
@@ -6526,6 +6527,10 @@
         const <DartType>[],
         sourceInformation: trySourceInformation);
     HInvokeStatic unwrappedException = kernelBuilder.pop();
+    unwrappedException.sideEffects
+      ..clearAllDependencies()
+      ..clearAllSideEffects();
+    unwrappedException.targetCanThrow = false;
     tryInstruction.exception = exception;
     int catchesIndex = 0;
 
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 886f1f0..a01c6bf 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1933,7 +1933,7 @@
   /// The type arguments passed in this static invocation.
   final List<DartType> typeArguments;
 
-  final bool targetCanThrow;
+  bool targetCanThrow;
 
   @override
   bool canThrow(AbstractValueDomain domain) => targetCanThrow;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 8877f33..fde9609 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -3327,6 +3327,14 @@
   bool newGvnCandidates = false;
   HGraph _graph;
 
+  // Blocks that can be reached via control flow not expressed by the basic
+  // block CFG. These are catch and finally blocks that are reached from some
+  // mid-block instruction that throws in the CFG region corresponding to the
+  // Dart language try-block. The value stored in the map is the HTry that owns
+  // the catch or finally block. This map is filled in on-the-fly since the HTry
+  // dominates the catch and finally so is visited first.
+  Map<HBasicBlock, HTry> _blocksWithImprecisePredecessors;
+
   SsaLoadElimination(this._closedWorld)
       : _fieldAnalysis = _closedWorld.fieldAnalysis;
 
@@ -3416,6 +3424,15 @@
       }
     }
 
+    // If the current block is a catch or finally block, it is reachable from
+    // any instruction in the try region that can generate an exception.
+    if (_blocksWithImprecisePredecessors != null) {
+      final tryInstruction = _blocksWithImprecisePredecessors[block];
+      if (tryInstruction != null) {
+        memorySet.killLocationsForExceptionEdge();
+      }
+    }
+
     memories[block.id] = memorySet;
     HInstruction instruction = block.first;
     while (instruction != null) {
@@ -3425,6 +3442,17 @@
     }
   }
 
+  @override
+  visitTry(HTry instruction) {
+    _blocksWithImprecisePredecessors ??= {};
+    if (instruction.catchBlock != null) {
+      _blocksWithImprecisePredecessors[instruction.catchBlock] = instruction;
+    }
+    if (instruction.finallyBlock != null) {
+      _blocksWithImprecisePredecessors[instruction.finallyBlock] = instruction;
+    }
+  }
+
   void checkNewGvnCandidates(HInstruction instruction, HInstruction existing) {
     if (newGvnCandidates) return;
     bool hasUseGvn(HInstruction insn) => insn.nonCheck().useGvn();
@@ -3712,6 +3740,30 @@
     return !nonEscapingReceivers.contains(receiver);
   }
 
+  /// Kills locations that are imprecise due to many possible edges from
+  /// instructions in the try region that can throw.
+  void killLocationsForExceptionEdge() {
+    // Ideally we would treat each strong (must) update in the try region as a
+    // weak (may) update at the catch block, but we don't track this. The
+    // conservative approximation is to kill everything.
+    //
+    // Aliases can be retained because they are not updated - they are generated
+    // by an allocation and are killed by escaping. There is an edge from any
+    // exit from the try region to the catch block which accounts for the kills
+    // via escapes. Similarly, array lengths don't have updates (set:length is
+    // modeled as an escape which kills the length on some path), so lengths
+    // don't need to be killed here.
+    //
+    // TODO(sra): A more precise accounting of the effects in the try region
+    // might improve precision.
+    fieldValues.forEach((key, map) {
+      if (key != MemoryFeature.length) {
+        map?.clear();
+      }
+    });
+    keyedValues.clear();
+  }
+
   void registerAllocation(HInstruction instruction) {
     assert(instruction == instruction.nonCheck());
     nonEscapingReceivers.add(instruction);
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index d25cbd8..24ac86f 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -584,7 +584,8 @@
 
   @override
   String visitExitTry(HExitTry node) {
-    return "ExitTry";
+    final targets = currentBlock.successors.map((block) => 'B${block.id}');
+    return "ExitTry: (${targets.join(', ')})";
   }
 
   @override
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index 6e63cb6..ad1c27b 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -39,7 +39,6 @@
   package_config: any
   path: any
   source_maps: any
-  test: any
   cli_util: any
   # Unpublished packages that can be used via path dependency
   async_helper:
diff --git a/pkg/compiler/test/kernel/common_test_utils.dart b/pkg/compiler/test/kernel/common_test_utils.dart
deleted file mode 100644
index ed7c9e5..0000000
--- a/pkg/compiler/test/kernel/common_test_utils.dart
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:io';
-
-import 'package:front_end/src/api_unstable/dart2js.dart'
-    show
-        CompilerOptions,
-        DiagnosticMessage,
-        computePlatformBinariesLocation,
-        kernelForProgram,
-        parseExperimentalArguments,
-        parseExperimentalFlags;
-import 'package:kernel/ast.dart';
-import 'package:kernel/text/ast_to_text.dart' show Printer;
-import 'package:kernel/target/targets.dart';
-import 'package:test/test.dart';
-
-import 'package:compiler/src/kernel/dart2js_target.dart' show Dart2jsTarget;
-
-/// Environment define to update expectation files on failures.
-const kUpdateExpectations = 'updateExpectations';
-
-/// Environment define to dump actual results alongside expectations.
-const kDumpActualResult = 'dump.actual.result';
-
-class TestingDart2jsTarget extends Dart2jsTarget {
-  TestingDart2jsTarget(TargetFlags flags) : super('dart2js', flags);
-}
-
-Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
-    {Target target,
-    bool enableSuperMixins = false,
-    List<String> experimentalFlags,
-    Map<String, String> environmentDefines}) async {
-  final platformKernel =
-      computePlatformBinariesLocation().resolve('dart2js_platform.dill');
-  target ??= TestingDart2jsTarget(TargetFlags());
-  environmentDefines ??= <String, String>{};
-  final options = CompilerOptions()
-    ..target = target
-    ..additionalDills = <Uri>[platformKernel]
-    ..environmentDefines = environmentDefines
-    ..explicitExperimentalFlags =
-        parseExperimentalFlags(parseExperimentalArguments(experimentalFlags),
-            onError: (String message) {
-      throw message;
-    })
-    ..onDiagnostic = (DiagnosticMessage message) {
-      fail("Compilation error: ${message.plainTextFormatted.join('\n')}");
-    };
-
-  final Component component =
-      (await kernelForProgram(sourceUri, options)).component;
-
-  // Make sure the library name is the same and does not depend on the order
-  // of test cases.
-  component.mainMethod.enclosingLibrary.name = '#lib';
-
-  return component;
-}
-
-String kernelLibraryToString(Library library) {
-  final buffer = StringBuffer();
-  Printer(buffer, showMetadata: true).writeLibraryFile(library);
-  return buffer
-      .toString()
-      .replaceAll(library.importUri.toString(), library.name);
-}
-
-String kernelComponentToString(Component component) {
-  final buffer = StringBuffer();
-  Printer(buffer, showMetadata: true).writeComponentFile(component);
-  final mainLibrary = component.mainMethod.enclosingLibrary;
-  return buffer
-      .toString()
-      .replaceAll(mainLibrary.importUri.toString(), mainLibrary.name);
-}
-
-class Difference {
-  final int line;
-  final String actual;
-  final String expected;
-
-  Difference(this.line, this.actual, this.expected);
-}
-
-Difference findFirstDifference(String actual, String expected) {
-  final actualLines = actual.split('\n');
-  final expectedLines = expected.split('\n');
-  int i = 0;
-  for (; i < actualLines.length && i < expectedLines.length; ++i) {
-    if (actualLines[i] != expectedLines[i]) {
-      return Difference(i + 1, actualLines[i], expectedLines[i]);
-    }
-  }
-  return Difference(i + 1, i < actualLines.length ? actualLines[i] : '<END>',
-      i < expectedLines.length ? expectedLines[i] : '<END>');
-}
-
-void compareResultWithExpectationsFile(Uri source, String actual) {
-  final expectFile = File(source.toFilePath() + '.expect');
-  final expected = expectFile.existsSync() ? expectFile.readAsStringSync() : '';
-
-  if (actual != expected) {
-    if (bool.fromEnvironment(kUpdateExpectations)) {
-      expectFile.writeAsStringSync(actual);
-      print("  Updated $expectFile");
-    } else {
-      if (bool.fromEnvironment(kDumpActualResult)) {
-        File(source.toFilePath() + '.actual').writeAsStringSync(actual);
-      }
-      Difference diff = findFirstDifference(actual, expected);
-      fail("""
-
-Result is different for the test case $source
-
-The first difference is at line ${diff.line}.
-Actual:   ${diff.actual}
-Expected: ${diff.expected}
-
-This failure can be caused by changes in the front-end if it starts generating
-different kernel AST for the same Dart programs.
-
-In order to re-generate expectations run tests with -D$kUpdateExpectations=true VM option:
-
-  sdk/bin/dart -D$kUpdateExpectations=true pkg/compiler/test/kernel/goldens_test.dart
-
-In order to dump actual results into .actual files run tests with -D$kDumpActualResult=true VM option.
-""");
-    }
-  }
-}
diff --git a/pkg/compiler/test/kernel/data/list_generate_1.dart.expect b/pkg/compiler/test/kernel/data/list_generate_1.dart.expect
deleted file mode 100644
index 62d51f9..0000000
--- a/pkg/compiler/test/kernel/data/list_generate_1.dart.expect
+++ /dev/null
@@ -1,32 +0,0 @@
-library #lib;
-import self as self;
-import "dart:core" as core;
-import "dart:_interceptors" as _in;
-
-static field core::List<core::int*>* list1 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-  }
-} =>_list;
-static field core::List<core::int*>* list2 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-  }
-} =>_list;
-static field core::List<core::int*>* list3 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateFixed<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-  }
-} =>_list;
-static field core::List<core::int*>* list4 = core::List::generate<core::int*>(10, (core::int* i) → core::int* => i, growable: self::someGrowable);
-static field core::bool* someGrowable = true;
-static method main() → void {
-  self::someGrowable = !self::someGrowable;
-  core::print(<core::List<core::int*>*>[self::list1, self::list2, self::list3, self::list4]);
-}
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
deleted file mode 100644
index c87b037..0000000
--- a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
+++ /dev/null
@@ -1,21 +0,0 @@
-library #lib;
-import self as self;
-import "dart:core" as core;
-import "dart:_interceptors" as _in;
-
-static method main() → void {
-  core::print( block {
-    final _in::JSArray<core::List<core::int*>*> _list = _in::JSArray::allocateGrowable<core::List<core::int*>*>(10);
-    for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-      core::int* i = i;
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, block {
-        final core::int _length = i;
-        final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(_length);
-        for (core::int i = 0; i.{core::num::<}(_length){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-          core::int* i = i;
-          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1){(core::num*) →* core::int*}){(core::int, core::int*) → void};
-        }
-      } =>_list){(core::int, core::List<core::int*>*) → void};
-    }
-  } =>_list);
-}
diff --git a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect b/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
deleted file mode 100644
index c8614fa..0000000
--- a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
+++ /dev/null
@@ -1,51 +0,0 @@
-library #lib;
-import self as self;
-import "dart:core" as core;
-import "dart:_interceptors" as _in;
-
-static field core::List<core::int*>* list1 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    {
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-    }
-  }
-} =>_list;
-static field core::List<core::int*>* list2 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    {
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-    }
-  }
-} =>_list;
-static field core::List<core::int*>* list3 = block {
-  final _in::JSArray<core::int*> _list = _in::JSArray::allocateFixed<core::int*>(10);
-  for (core::int i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int) →* core::int}) {
-    core::int* i = i;
-    {
-      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int*) → void};
-    }
-  }
-} =>_list;
-static field core::List<core::int*>* list4 = core::List::generate<core::int*>(10, (core::int* i) → core::int* {
-  return i;
-}, growable: self::someGrowable);
-static field core::List<core::int*>* list5 = core::List::generate<core::int*>(10, (core::int* i) → core::int* {
-  if(i.{core::int::isEven}{core::bool*})
-    return i.{core::num::+}(1){(core::num*) →* core::int*};
-  return i.{core::num::-}(1){(core::num*) →* core::int*};
-});
-static field core::List<core::int*>* list6 = core::List::generate<core::int*>(10, #C1);
-static field core::List<core::int*>* list7 = core::List::generate<core::int*>(10, self::bar);
-static field core::bool* someGrowable = true;
-static method foo(core::int* i) → core::int*
-  return i;
-static get bar() → (core::int*) →* core::int*
-  return #C1;
-static method main() → void {
-  self::someGrowable = !self::someGrowable;
-  core::print(<core::List<core::int*>*>[self::list1, self::list2, self::list3, self::list4, self::list5, self::list6, self::list7]);
-}
diff --git a/pkg/compiler/test/kernel/goldens_test.dart b/pkg/compiler/test/kernel/goldens_test.dart
deleted file mode 100644
index dcef219..0000000
--- a/pkg/compiler/test/kernel/goldens_test.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:io';
-
-import 'package:kernel/target/targets.dart';
-import 'package:kernel/ast.dart';
-import 'package:kernel/kernel.dart';
-import 'package:test/test.dart';
-
-import 'common_test_utils.dart';
-
-final String testRootDir = Platform.script.resolve('.').toFilePath();
-
-runTestCase(
-    Uri source, List<String> experimentalFlags, bool soundNullSafety) async {
-  final target =
-      TestingDart2jsTarget(TargetFlags(enableNullSafety: soundNullSafety));
-  Component component = await compileTestCaseToKernelProgram(source,
-      target: target, experimentalFlags: experimentalFlags);
-
-  String actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);
-
-  compareResultWithExpectationsFile(source, actual);
-}
-
-main() {
-  group('goldens', () {
-    final testCasesDir = new Directory(testRootDir + '/data');
-
-    for (var entry
-        in testCasesDir.listSync(recursive: true, followLinks: false)) {
-      final path = entry.path;
-      if (path.endsWith('.dart')) {
-        final bool unsoundNullSafety = path.endsWith('_unsound.dart');
-        test(
-            path,
-            () => runTestCase(
-                entry.uri, const ['non-nullable'], !unsoundNullSafety));
-      }
-    }
-  }, timeout: Timeout.none);
-}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 2ee0aa7..dd9f732 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1539,6 +1539,7 @@
 inout
 input
 inputs
+inscrutable
 insert
 inserted
 inserting
diff --git a/pkg/compiler/test/kernel/data/list_generate_1.dart b/pkg/front_end/testcases/dart2js/list_generate_1.dart
similarity index 100%
rename from pkg/compiler/test/kernel/data/list_generate_1.dart
rename to pkg/front_end/testcases/dart2js/list_generate_1.dart
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.expect
new file mode 100644
index 0000000..ae26523
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1 = core::List::generate<core::int>(10, (core::int i) → core::int => i);
+static field core::List<core::int> list2 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: true);
+static field core::List<core::int> list3 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: false);
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: self::someGrowable);
+static field core::bool someGrowable = true;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4]);
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.transformed.expect
new file mode 100644
index 0000000..037687b
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.strong.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static field core::List<core::int> list1 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list2 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list3 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateFixed<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: self::someGrowable);
+static field core::bool someGrowable = true;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4]);
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline.expect
new file mode 100644
index 0000000..d940e13
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline.expect
@@ -0,0 +1,6 @@
+var list1 = List<int>.generate(10, (i) => i);
+var list2 = List<int>.generate(10, (i) => i, growable: true);
+var list3 = List<int>.generate(10, (i) => i, growable: false);
+var list4 = List<int>.generate(10, (i) => i, growable: someGrowable);
+bool someGrowable = true;
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..4ed37d0
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.textual_outline_modelled.expect
@@ -0,0 +1,6 @@
+bool someGrowable = true;
+var list1 = List<int>.generate(10, (i) => i);
+var list2 = List<int>.generate(10, (i) => i, growable: true);
+var list3 = List<int>.generate(10, (i) => i, growable: false);
+var list4 = List<int>.generate(10, (i) => i, growable: someGrowable);
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.expect
new file mode 100644
index 0000000..ae26523
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.expect
@@ -0,0 +1,13 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1 = core::List::generate<core::int>(10, (core::int i) → core::int => i);
+static field core::List<core::int> list2 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: true);
+static field core::List<core::int> list3 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: false);
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: self::someGrowable);
+static field core::bool someGrowable = true;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4]);
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.outline.expect
new file mode 100644
index 0000000..fd26d48
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.outline.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1;
+static field core::List<core::int> list2;
+static field core::List<core::int> list3;
+static field core::List<core::int> list4;
+static field core::bool someGrowable;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.transformed.expect
new file mode 100644
index 0000000..037687b
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_1.dart.weak.transformed.expect
@@ -0,0 +1,32 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static field core::List<core::int> list1 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list2 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list3 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateFixed<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+  }
+} =>_list;
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int => i, growable: self::someGrowable);
+static field core::bool someGrowable = true;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4]);
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart b/pkg/front_end/testcases/dart2js/list_generate_2.dart
similarity index 100%
rename from pkg/compiler/test/kernel/data/list_generate_2.dart
rename to pkg/front_end/testcases/dart2js/list_generate_2.dart
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.expect
new file mode 100644
index 0000000..d3c85ea
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.expect
@@ -0,0 +1,7 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method main() → void {
+  core::print(core::List::generate<core::List<core::int>>(10, (core::int i) → core::List<core::int> => core::List::generate<core::int>(i, (core::int i) → core::int => i.{core::num::+}(1){(core::num) → core::int})));
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..3f9b290
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.strong.transformed.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static method main() → void {
+  core::print( block {
+    final _in::JSArray<core::List<core::int>> _list = _in::JSArray::allocateGrowable<core::List<core::int>>(10);
+    for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+      core::int i = i;
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, block {
+        final core::int* _length = i;
+        final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(_length);
+        for (core::int* i = 0; i.{core::num::<}(_length){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+          core::int i = i;
+          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1){(core::num) → core::int}){(core::int, core::int) → void};
+        }
+      } =>_list){(core::int, core::List<core::int>) → void};
+    }
+  } =>_list);
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline.expect
new file mode 100644
index 0000000..ab73b3a
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline.expect
@@ -0,0 +1 @@
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..ab73b3a
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.textual_outline_modelled.expect
@@ -0,0 +1 @@
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.expect
new file mode 100644
index 0000000..d3c85ea
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.expect
@@ -0,0 +1,7 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method main() → void {
+  core::print(core::List::generate<core::List<core::int>>(10, (core::int i) → core::List<core::int> => core::List::generate<core::int>(i, (core::int i) → core::int => i.{core::num::+}(1){(core::num) → core::int})));
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.outline.expect
new file mode 100644
index 0000000..684454e
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.outline.expect
@@ -0,0 +1,5 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..3f9b290
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_2.dart.weak.transformed.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static method main() → void {
+  core::print( block {
+    final _in::JSArray<core::List<core::int>> _list = _in::JSArray::allocateGrowable<core::List<core::int>>(10);
+    for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+      core::int i = i;
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, block {
+        final core::int* _length = i;
+        final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(_length);
+        for (core::int* i = 0; i.{core::num::<}(_length){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+          core::int i = i;
+          _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1){(core::num) → core::int}){(core::int, core::int) → void};
+        }
+      } =>_list){(core::int, core::List<core::int>) → void};
+    }
+  } =>_list);
+}
diff --git a/pkg/compiler/test/kernel/data/list_generate_3.dart b/pkg/front_end/testcases/dart2js/list_generate_3.dart
similarity index 100%
rename from pkg/compiler/test/kernel/data/list_generate_3.dart
rename to pkg/front_end/testcases/dart2js/list_generate_3.dart
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.expect
new file mode 100644
index 0000000..4f4c5d2
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+});
+static field core::List<core::int> list2 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: true);
+static field core::List<core::int> list3 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: false);
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: self::someGrowable);
+static field core::List<core::int> list5 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  if(i.{core::int::isEven}{core::bool})
+    return i.{core::num::+}(1){(core::num) → core::int};
+  return i.{core::num::-}(1){(core::num) → core::int};
+});
+static field core::List<core::int> list6 = core::List::generate<core::int>(10, #C1);
+static field core::List<core::int> list7 = core::List::generate<core::int>(10, self::bar);
+static field core::bool someGrowable = true;
+static method foo(core::int i) → core::int
+  return i;
+static get bar() → (core::int) → core::int
+  return #C1;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4, self::list5, self::list6, self::list7]);
+}
+
+constants  {
+  #C1 = static-tearoff self::foo
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.transformed.expect
new file mode 100644
index 0000000..8fbe3a6
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.strong.transformed.expect
@@ -0,0 +1,55 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static field core::List<core::int> list1 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list2 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list3 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateFixed<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: self::someGrowable);
+static field core::List<core::int> list5 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  if(i.{core::int::isEven}{core::bool})
+    return i.{core::num::+}(1){(core::num) → core::int};
+  return i.{core::num::-}(1){(core::num) → core::int};
+});
+static field core::List<core::int> list6 = core::List::generate<core::int>(10, #C1);
+static field core::List<core::int> list7 = core::List::generate<core::int>(10, self::bar);
+static field core::bool someGrowable = true;
+static method foo(core::int i) → core::int
+  return i;
+static get bar() → (core::int) → core::int
+  return #C1;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4, self::list5, self::list6, self::list7]);
+}
+
+constants  {
+  #C1 = static-tearoff self::foo
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline.expect
new file mode 100644
index 0000000..432cc1c
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline.expect
@@ -0,0 +1,22 @@
+var list1 = List<int>.generate(10, (i) {
+  return i;
+});
+var list2 = List<int>.generate(10, (i) {
+  return i;
+}, growable: true);
+var list3 = List<int>.generate(10, (i) {
+  return i;
+}, growable: false);
+var list4 = List<int>.generate(10, (i) {
+  return i;
+}, growable: someGrowable);
+var list5 = List<int>.generate(10, (i) {
+  if (i.isEven) return i + 1;
+  return i - 1;
+});
+var list6 = List<int>.generate(10, foo);
+int foo(int i) => i;
+var list7 = List<int>.generate(10, bar);
+int Function(int) get bar => foo;
+bool someGrowable = true;
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..79c5b5f
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.textual_outline_modelled.expect
@@ -0,0 +1,22 @@
+bool someGrowable = true;
+int Function(int) get bar => foo;
+int foo(int i) => i;
+var list1 = List<int>.generate(10, (i) {
+  return i;
+});
+var list2 = List<int>.generate(10, (i) {
+  return i;
+}, growable: true);
+var list3 = List<int>.generate(10, (i) {
+  return i;
+}, growable: false);
+var list4 = List<int>.generate(10, (i) {
+  return i;
+}, growable: someGrowable);
+var list5 = List<int>.generate(10, (i) {
+  if (i.isEven) return i + 1;
+  return i - 1;
+});
+var list6 = List<int>.generate(10, foo);
+var list7 = List<int>.generate(10, bar);
+void main() {}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.expect
new file mode 100644
index 0000000..4f4c5d2
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+});
+static field core::List<core::int> list2 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: true);
+static field core::List<core::int> list3 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: false);
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: self::someGrowable);
+static field core::List<core::int> list5 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  if(i.{core::int::isEven}{core::bool})
+    return i.{core::num::+}(1){(core::num) → core::int};
+  return i.{core::num::-}(1){(core::num) → core::int};
+});
+static field core::List<core::int> list6 = core::List::generate<core::int>(10, #C1);
+static field core::List<core::int> list7 = core::List::generate<core::int>(10, self::bar);
+static field core::bool someGrowable = true;
+static method foo(core::int i) → core::int
+  return i;
+static get bar() → (core::int) → core::int
+  return #C1;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4, self::list5, self::list6, self::list7]);
+}
+
+constants  {
+  #C1 = static-tearoff self::foo
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.outline.expect
new file mode 100644
index 0000000..63bf114
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.outline.expect
@@ -0,0 +1,18 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static field core::List<core::int> list1;
+static field core::List<core::int> list2;
+static field core::List<core::int> list3;
+static field core::List<core::int> list4;
+static field core::List<core::int> list5;
+static field core::List<core::int> list6;
+static field core::List<core::int> list7;
+static field core::bool someGrowable;
+static method foo(core::int i) → core::int
+  ;
+static get bar() → (core::int) → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.transformed.expect
new file mode 100644
index 0000000..8fbe3a6
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/list_generate_3.dart.weak.transformed.expect
@@ -0,0 +1,55 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+static field core::List<core::int> list1 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list2 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list3 = block {
+  final _in::JSArray<core::int> _list = _in::JSArray::allocateFixed<core::int>(10);
+  for (core::int* i = 0; i.{core::num::<}(10){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::int*) → core::int*}) {
+    core::int i = i;
+    {
+      _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i){(core::int, core::int) → void};
+    }
+  }
+} =>_list;
+static field core::List<core::int> list4 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  return i;
+}, growable: self::someGrowable);
+static field core::List<core::int> list5 = core::List::generate<core::int>(10, (core::int i) → core::int {
+  if(i.{core::int::isEven}{core::bool})
+    return i.{core::num::+}(1){(core::num) → core::int};
+  return i.{core::num::-}(1){(core::num) → core::int};
+});
+static field core::List<core::int> list6 = core::List::generate<core::int>(10, #C1);
+static field core::List<core::int> list7 = core::List::generate<core::int>(10, self::bar);
+static field core::bool someGrowable = true;
+static method foo(core::int i) → core::int
+  return i;
+static get bar() → (core::int) → core::int
+  return #C1;
+static method main() → void {
+  self::someGrowable = !self::someGrowable;
+  core::print(<core::List<core::int>>[self::list1, self::list2, self::list3, self::list4, self::list5, self::list6, self::list7]);
+}
+
+constants  {
+  #C1 = static-tearoff self::foo
+}
diff --git a/sdk/lib/_http/websocket.dart b/sdk/lib/_http/websocket.dart
index c9c6246..4b9bbff 100644
--- a/sdk/lib/_http/websocket.dart
+++ b/sdk/lib/_http/websocket.dart
@@ -375,8 +375,10 @@
           {Iterable<String>? protocols,
           Map<String, dynamic>? headers,
           CompressionOptions compression =
-              CompressionOptions.compressionDefault}) =>
-      _WebSocketImpl.connect(url, protocols, headers, compression: compression);
+              CompressionOptions.compressionDefault,
+          HttpClient? customClient}) =>
+      _WebSocketImpl.connect(url, protocols, headers,
+          compression: compression, customClient: customClient);
 
   @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`'
       ' instead of `extends` if implementing this abstract class.')
diff --git a/sdk/lib/_http/websocket_impl.dart b/sdk/lib/_http/websocket_impl.dart
index 7de33f0..fe2c5a7 100644
--- a/sdk/lib/_http/websocket_impl.dart
+++ b/sdk/lib/_http/websocket_impl.dart
@@ -999,8 +999,8 @@
 
   static Future<WebSocket> connect(
       String url, Iterable<String>? protocols, Map<String, dynamic>? headers,
-      {CompressionOptions compression =
-          CompressionOptions.compressionDefault}) {
+      {CompressionOptions compression = CompressionOptions.compressionDefault,
+      HttpClient? customClient}) {
     Uri uri = Uri.parse(url);
     if (uri.scheme != "ws" && uri.scheme != "wss") {
       throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
@@ -1024,7 +1024,7 @@
         path: uri.path,
         query: uri.query,
         fragment: uri.fragment);
-    return _httpClient.openUrl("GET", uri).then((request) {
+    return (customClient ?? _httpClient).openUrl("GET", uri).then((request) {
       if (uri.userInfo != null && !uri.userInfo.isEmpty) {
         // If the URL contains user information use that for basic
         // authorization.
diff --git a/tests/language/regress/regress47166_test.dart b/tests/language/regress/regress47166_test.dart
new file mode 100644
index 0000000..b44d28b
--- /dev/null
+++ b/tests/language/regress/regress47166_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/47166
+
+import 'package:expect/expect.dart';
+
+@pragma('dart2js:noInline')
+void maybeThrow(bool doThrow) {
+  if (doThrow) throw Exception('omg!');
+}
+
+int triggerTheProblem(bool doThrow) {
+  int x = 1;
+  try {
+    x = 100;
+    maybeThrow(doThrow);
+    x = 1; // unreached.
+    log1 = x;
+  } catch (e) {
+    log2 = x;
+  }
+  log3 = x;
+
+  // This closure creates a context object ('box') subject to load elimination.
+  // In the reported bug, log2 and log3 were assigned constant '1' from an
+  // incorrect store-forwarding optimization of the boxed 'x'. The '1' came from
+  // the merge of '1' from the initialization of 'x' and the unreachable
+  // assignment of 'x'.
+  g = () => x;
+
+  log4 = x;
+  return x;
+}
+
+dynamic g;
+int log1 = 0, log2 = 0, log3 = 0, log4 = 0, log5 = 0, log6 = 0;
+
+void main() {
+  log5 = triggerTheProblem(true);
+  log6 = g(); // Use 'g'.
+  Expect.equals(
+      'log1=0 log2=100 log3=100 log4=100 log5=100 log6=100',
+      'log1=$log1 log2=$log2 log3=$log3 log4=$log4 log5=$log5 log6=$log6',
+      'throwing');
+
+  // Run the test with 'doThrow' being false to avoid any confounding
+  // optimizations due to constant propagation.
+  log1 = log2 = log3 = log4 = log5 = log6 = 0;
+  log5 = triggerTheProblem(false);
+  log6 = g(); // Use 'g'.
+  Expect.equals(
+      'log1=1 log2=0 log3=1 log4=1 log5=1 log6=1',
+      'log1=$log1 log2=$log2 log3=$log3 log4=$log4 log5=$log5 log6=$log6',
+      'not throwing');
+}
diff --git a/tests/standalone/io/web_socket_error_test.dart b/tests/standalone/io/web_socket_error_test.dart
index a7fdd0e..aca2548 100644
--- a/tests/standalone/io/web_socket_error_test.dart
+++ b/tests/standalone/io/web_socket_error_test.dart
@@ -48,8 +48,8 @@
       : HttpServer.bind(HOST_NAME, 0, backlog: backlog);
 
   Future<WebSocket> createClient(int port) =>
-      // TODO(whesse): Add a client context argument to WebSocket.connect.
-      WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/');
+      WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/',
+          customClient: secure ? HttpClient(context: clientContext) : null);
 
   void testForceCloseServerEnd(int totalConnections) {
     createServer().then((server) {
@@ -94,7 +94,6 @@
 main() {
   asyncStart();
   new SecurityConfiguration(secure: false).runTests();
-  // TODO(whesse): WebSocket.connect needs an optional context: parameter
-  // new SecurityConfiguration(secure: true).runTests();
+  new SecurityConfiguration(secure: true).runTests();
   asyncEnd();
 }
diff --git a/tests/standalone/io/web_socket_test.dart b/tests/standalone/io/web_socket_test.dart
index 9b0e789..f5927ba 100644
--- a/tests/standalone/io/web_socket_test.dart
+++ b/tests/standalone/io/web_socket_test.dart
@@ -13,10 +13,8 @@
 import "dart:typed_data";
 
 import "package:async_helper/async_helper.dart";
-import "package:convert/convert.dart";
 import "package:crypto/crypto.dart";
 import "package:expect/expect.dart";
-import "package:path/path.dart";
 
 const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
@@ -44,9 +42,17 @@
       ? HttpServer.bindSecure(HOST_NAME, 0, serverContext, backlog: backlog)
       : HttpServer.bind(HOST_NAME, 0, backlog: backlog);
 
-  Future<WebSocket> createClient(int port) =>
-      // TODO(whesse): Add client context argument to WebSocket.connect
-      WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/');
+  Future<WebSocket> createClient(int port,
+          {String? user,
+          Map<String, Object>? headers,
+          String? customUserAgent}) =>
+      WebSocket.connect(
+          '${secure ? "wss" : "ws"}://${user is Null ? "" : "$user@"}$HOST_NAME:$port/',
+          headers: headers,
+          customClient: secure
+              ? (HttpClient(context: clientContext)
+                ..userAgent = customUserAgent)
+              : null);
 
   checkCloseStatus(webSocket, closeStatus, closeReason) {
     Expect.equals(
@@ -304,7 +310,7 @@
         asyncEnd();
       });
 
-      HttpClient client = new HttpClient();
+      final client = HttpClient(context: secure ? clientContext : null);
       client
           .postUrl(Uri.parse(
               "${secure ? 'https:' : 'http:'}//$HOST_NAME:${server.port}/"))
@@ -401,12 +407,12 @@
       var baseWsUrl = '$wsProtocol://$HOST_NAME:${server.port}/';
       var httpProtocol = '${secure ? "https" : "http"}';
       var baseHttpUrl = '$httpProtocol://$HOST_NAME:${server.port}/';
-      HttpClient client = new HttpClient();
+      final client = HttpClient(context: secure ? clientContext : null);
 
       for (int i = 0; i < connections; i++) {
         var completer = new Completer();
         futures.add(completer.future);
-        WebSocket.connect('${baseWsUrl}').then((websocket) {
+        createClient(server.port).then((websocket) {
           websocket.listen((_) {
             websocket.close();
           }, onDone: completer.complete);
@@ -456,9 +462,7 @@
         });
       });
 
-      var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/';
-
-      WebSocket.connect(url).then((websocket) {
+      createClient(server.port).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -484,12 +488,11 @@
         });
       });
 
-      var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/';
       var headers = {
         'My-Header': 'my-value',
         'My-Header-Multiple': ['my-value-1', 'my-value-2']
       };
-      WebSocket.connect(url, headers: headers).then((websocket) {
+      createClient(server.port, headers: headers).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -522,9 +525,7 @@
         });
       });
 
-      var url =
-          '${secure ? "wss" : "ws"}://$userInfo@$HOST_NAME:${server.port}/';
-      WebSocket.connect(url).then((websocket) {
+      createClient(server.port, user: userInfo).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -554,6 +555,24 @@
     });
   }
 
+  void testStaticClientUserAgentStaysTheSame() {
+    asyncStart();
+    createServer().then((server) {
+      server.transform(new WebSocketTransformer()).listen((webSocket) {
+        Expect.equals('Custom User Agent', WebSocket.userAgent);
+        server.close();
+        webSocket.close();
+        asyncEnd();
+      });
+      // Next line should take no effect on custom user agent value provided
+      WebSocket.userAgent = 'Custom User Agent';
+      createClient(server.port, customUserAgent: 'New User Agent')
+          .then((webSocket) {
+        webSocket.close();
+      });
+    });
+  }
+
   void runTests() {
     testRequestResponseClientCloses(2, null, null, 1);
     testRequestResponseClientCloses(2, 3001, null, 2);
@@ -582,11 +601,11 @@
     testAdditionalHeaders();
     testBasicAuthentication();
     testShouldSetUserAgent();
+    testStaticClientUserAgentStaysTheSame();
   }
 }
 
 main() {
   new SecurityConfiguration(secure: false).runTests();
-  // TODO(whesse): Make WebSocket.connect() take an optional context: parameter.
-  // new SecurityConfiguration(secure: true).runTests();
+  new SecurityConfiguration(secure: true).runTests();
 }
diff --git a/tests/standalone_2/io/web_socket_test.dart b/tests/standalone_2/io/web_socket_test.dart
index a931549..5d7f425 100644
--- a/tests/standalone_2/io/web_socket_test.dart
+++ b/tests/standalone_2/io/web_socket_test.dart
@@ -15,10 +15,8 @@
 import "dart:typed_data";
 
 import "package:async_helper/async_helper.dart";
-import "package:convert/convert.dart";
 import "package:crypto/crypto.dart";
 import "package:expect/expect.dart";
-import "package:path/path.dart";
 
 const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
@@ -46,9 +44,15 @@
       ? HttpServer.bindSecure(HOST_NAME, 0, serverContext, backlog: backlog)
       : HttpServer.bind(HOST_NAME, 0, backlog: backlog);
 
-  Future<WebSocket> createClient(int port) =>
-      // TODO(whesse): Add client context argument to WebSocket.connect
-      WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/');
+  Future<WebSocket> createClient(int port,
+          {String user, Map<String, Object> headers, String customUserAgent}) =>
+      WebSocket.connect(
+          '${secure ? "wss" : "ws"}://${user is Null ? "" : "$user@"}$HOST_NAME:$port/',
+          headers: headers,
+          customClient: secure
+              ? (HttpClient(context: clientContext)
+                ..userAgent = customUserAgent)
+              : null);
 
   checkCloseStatus(webSocket, closeStatus, closeReason) {
     Expect.equals(
@@ -306,7 +310,7 @@
         asyncEnd();
       });
 
-      HttpClient client = new HttpClient();
+      final client = HttpClient(context: secure ? clientContext : null);
       client
           .postUrl(Uri.parse(
               "${secure ? 'https:' : 'http:'}//$HOST_NAME:${server.port}/"))
@@ -403,12 +407,12 @@
       var baseWsUrl = '$wsProtocol://$HOST_NAME:${server.port}/';
       var httpProtocol = '${secure ? "https" : "http"}';
       var baseHttpUrl = '$httpProtocol://$HOST_NAME:${server.port}/';
-      HttpClient client = new HttpClient();
+      final client = HttpClient(context: secure ? clientContext : null);
 
       for (int i = 0; i < connections; i++) {
         var completer = new Completer();
         futures.add(completer.future);
-        WebSocket.connect('${baseWsUrl}').then((websocket) {
+        createClient(server.port).then((websocket) {
           websocket.listen((_) {
             websocket.close();
           }, onDone: completer.complete);
@@ -458,9 +462,7 @@
         });
       });
 
-      var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/';
-
-      WebSocket.connect(url).then((websocket) {
+      createClient(server.port).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -486,12 +488,11 @@
         });
       });
 
-      var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/';
       var headers = {
         'My-Header': 'my-value',
         'My-Header-Multiple': ['my-value-1', 'my-value-2']
       };
-      WebSocket.connect(url, headers: headers).then((websocket) {
+      createClient(server.port, headers: headers).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -524,9 +525,7 @@
         });
       });
 
-      var url =
-          '${secure ? "wss" : "ws"}://$userInfo@$HOST_NAME:${server.port}/';
-      WebSocket.connect(url).then((websocket) {
+      createClient(server.port, user: userInfo).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
           websocket.close();
@@ -556,6 +555,24 @@
     });
   }
 
+  void testStaticClientUserAgentStaysTheSame() {
+    asyncStart();
+    createServer().then((server) {
+      server.transform(new WebSocketTransformer()).listen((webSocket) {
+        Expect.equals('Custom User Agent', WebSocket.userAgent);
+        server.close();
+        webSocket.close();
+        asyncEnd();
+      });
+      // Next line should take no effect on custom user agent value provided
+      WebSocket.userAgent = 'Custom User Agent';
+      createClient(server.port, customUserAgent: 'New User Agent')
+          .then((webSocket) {
+        webSocket.close();
+      });
+    });
+  }
+
   void runTests() {
     testRequestResponseClientCloses(2, null, null, 1);
     testRequestResponseClientCloses(2, 3001, null, 2);
@@ -584,11 +601,11 @@
     testAdditionalHeaders();
     testBasicAuthentication();
     testShouldSetUserAgent();
+    testStaticClientUserAgentStaysTheSame();
   }
 }
 
 main() {
   new SecurityConfiguration(secure: false).runTests();
-  // TODO(whesse): Make WebSocket.connect() take an optional context: parameter.
-  // new SecurityConfiguration(secure: true).runTests();
+  new SecurityConfiguration(secure: true).runTests();
 }
diff --git a/tools/VERSION b/tools/VERSION
index 51b89ce..560e5c9 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 111
+PRERELEASE 112
 PRERELEASE_PATCH 0
\ No newline at end of file