Version 2.18.0-202.0.dev

Merge commit '98a428787681916434191bd3b87478b0cbc97458' into 'dev'
diff --git a/DEPS b/DEPS
index 89a6b62..d3febf3 100644
--- a/DEPS
+++ b/DEPS
@@ -133,7 +133,7 @@
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_rev": "c40cc32eabecb9d60f1045d1403108d968805f9a",
   "protobuf_rev": "3149f6f2d323e11dbcc983b7ac8b3b9e9d686293",
-  "pub_rev": "c4e9ddc888c3aa89ef4462f0c4298929191e32b9",
+  "pub_rev": "9bf4289d6fd5d6872a8929d6312bbd7098f3ea9c",
   "pub_semver_rev": "5c0b4bfd5ca57fe16f1319c581dc8c882e9b8cb2",
   "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
   "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
index e4ede99..b499ca5 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
@@ -53,14 +53,14 @@
         .where((procedure) => procedure.name.text == stubName);
     if (stubs.isEmpty) {
       // Note that the return type of the cloned function is transformed.
-      var functionNode =
-          super.visitFunctionNode(_cloner.clone(factoryTarget.function))
-              as FunctionNode;
+      var functionNode = super
+              .visitFunctionNode(_cloner.cloneInContext(factoryTarget.function))
+          as FunctionNode;
       var staticMethod = Procedure(
           Name(stubName), ProcedureKind.Method, functionNode,
           isStatic: true, fileUri: factoryTarget.fileUri)
-        ..parent = factoryClass;
-      factoryClass.procedures.add(staticMethod);
+        ..fileOffset = factoryTarget.fileOffset;
+      factoryClass.addProcedure(staticMethod);
       return staticMethod;
     } else {
       assert(stubs.length == 1);
diff --git a/pkg/compiler/test/end_to_end/modular_loader_test.dart b/pkg/compiler/test/end_to_end/modular_loader_test.dart
index 33fc298..11132f9 100644
--- a/pkg/compiler/test/end_to_end/modular_loader_test.dart
+++ b/pkg/compiler/test/end_to_end/modular_loader_test.dart
@@ -94,8 +94,7 @@
   var component = (await kernelForModule(inputUris, options)).component;
   for (var lib in component.libraries) {
     if (!inputUriSet.contains(lib.importUri)) {
-      component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
-      lib.computeCanonicalNames();
+      lib.bindCanonicalNames(component.root);
     }
   }
   return serializeComponent(component,
diff --git a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
index f30a919..c084dd8 100644
--- a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
+++ b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
@@ -179,8 +179,7 @@
         // be computed as part of serialization, so we need to do that
         // preemptively here to avoid errors when serializing references to
         // elements of these libraries.
-        component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
-        lib.computeCanonicalNames();
+        lib.bindCanonicalNames(component.root);
       }
     }
   }
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 0874509..ae6bc65 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -405,30 +405,36 @@
     if (kernelTextSerialization) {
       steps.add(const KernelTextSerialization());
     }
-    if (compileMode == CompileMode.full) {
-      steps.add(const Transform());
-      steps.add(const Verify(CompileMode.full));
-      steps.add(const StressConstantEvaluatorStep());
-      if (!ignoreExpectations) {
-        steps.add(new MatchExpectation("$prefix$infix.transformed.expect",
-            serializeFirst: false, isLastMatchStep: updateExpectations));
-        if (!updateExpectations) {
+    switch (compileMode) {
+      case CompileMode.full:
+        steps.add(const Transform());
+        steps.add(const Verify(CompileMode.full));
+        steps.add(const StressConstantEvaluatorStep());
+        if (!ignoreExpectations) {
           steps.add(new MatchExpectation("$prefix$infix.transformed.expect",
-              serializeFirst: true, isLastMatchStep: true));
+              serializeFirst: false, isLastMatchStep: updateExpectations));
+          if (!updateExpectations) {
+            steps.add(new MatchExpectation("$prefix$infix.transformed.expect",
+                serializeFirst: true, isLastMatchStep: true));
+          }
         }
-      }
-      steps.add(const EnsureNoErrors());
-      steps.add(new WriteDill(skipVm: skipVm));
-      if (semiFuzz) {
-        steps.add(const FuzzCompiles());
-      }
+        steps.add(const EnsureNoErrors());
+        steps.add(new WriteDill(skipVm: skipVm));
+        if (semiFuzz) {
+          steps.add(const FuzzCompiles());
+        }
 
-      // Notice: The below steps will run async, i.e. the next test will run
-      // intertwined with this/these step(s). That for instance means that they
-      // should not touch any ASTs!
-      if (!skipVm) {
-        steps.add(const Run());
-      }
+        // Notice: The below steps will run async, i.e. the next test will run
+        // intertwined with this/these step(s). That for instance means that they
+        // should not touch any ASTs!
+        if (!skipVm) {
+          steps.add(const Run());
+        }
+        break;
+      case CompileMode.modular:
+      case CompileMode.outline:
+        steps.add(new WriteDill(skipVm: true));
+        break;
     }
   }
 
diff --git a/pkg/front_end/test/utils/kernel_chain.dart b/pkg/front_end/test/utils/kernel_chain.dart
index b0941a1..58b15da 100644
--- a/pkg/front_end/test/utils/kernel_chain.dart
+++ b/pkg/front_end/test/utils/kernel_chain.dart
@@ -228,7 +228,7 @@
 
     Component componentToText = component;
     if (serializeFirst) {
-      component.computeCanonicalNames();
+      // TODO(johnniwinther): Use library filter instead.
       List<Library> sdkLibraries =
           component.libraries.where((l) => !result.isUserLibrary(l)).toList();
 
@@ -442,7 +442,7 @@
   Future<Result<ComponentResult>> run(ComponentResult result, _) async {
     Component component = result.component;
     Procedure? mainMethod = component.mainMethod;
-    bool writeToFile = true;
+    bool writeToFile = !skipVm;
     if (mainMethod == null) {
       writeToFile = false;
     } else {
@@ -455,33 +455,60 @@
     ByteSink sink = new ByteSink();
     bool good = false;
     try {
+      // TODO(johnniwinther): Use library filter instead.
       // Avoid serializing the sdk.
-      component.computeCanonicalNames();
       Component userCode = new Component(
           nameRoot: component.root,
           uriToSource: new Map<Uri, Source>.from(component.uriToSource));
       userCode.setMainMethodAndMode(
           component.mainMethodName, true, component.mode);
+      List<Library> auxiliaryLibraries = [];
       for (Library library in component.libraries) {
+        bool includeLibrary;
         if (library.importUri.isScheme("dart")) {
           if (result.isUserLibrary(library)) {
             // dart:test, test:extra etc as used will say yes to being a user
             // library.
+            includeLibrary = true;
           } else if (library.isSynthetic) {
             // OK --- serialize that.
+            includeLibrary = true;
           } else {
             // Skip serialization of "real" platform libraries.
-            continue;
+            includeLibrary = false;
           }
+        } else if (result.isUserLibrary(library)) {
+          includeLibrary = true;
+        } else {
+          // This library is neither part of the user libraries nor part of the
+          // platform libraries. To run this, we need to include it in the
+          // dill.
+          auxiliaryLibraries.add(library);
+          includeLibrary = false;
         }
-        userCode.libraries.add(library);
+        if (includeLibrary) {
+          userCode.libraries.add(library);
+        }
       }
+
+      // We first ensure that we can serialize with possible references to
+      // libraries that aren't included in the serialization.
       new BinaryPrinter(sink).writeComponentFile(userCode);
+
+      // We then serialize with any such libraries to
+      //   a) ensure that we can do that too, and that
+      //   b) the output is complete (modulo the platform) so that the VM can
+      //      actually run it.
+      if (auxiliaryLibraries.isNotEmpty) {
+        userCode.libraries.addAll(auxiliaryLibraries);
+        sink = new ByteSink();
+        new BinaryPrinter(sink).writeComponentFile(userCode);
+      }
       good = true;
     } catch (e, s) {
       return fail(result, e, s);
     } finally {
-      if (good && writeToFile && !skipVm) {
+      if (good && writeToFile) {
         Directory tmp = await Directory.systemTemp.createTemp();
         Uri uri = tmp.uri.resolve("generated.dill");
         File generated = new File.fromUri(uri);
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart
new file mode 100644
index 0000000..2620a48
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, 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 'main_lib.dart';
+
+void main() {
+  setUp();
+  var staticJs = StaticJSClass.factory();
+}
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.expect
new file mode 100644
index 0000000..39fa210
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.expect
@@ -0,0 +1,56 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "main_lib.dart" as sta;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void {
+  sta::setUp();
+  sta::StaticJSClass staticJs = sta::StaticJSClass::factory();
+}
+
+@#C2
+library static_interop /*isNonNullableByDefault*/;
+import self as sta;
+import "package:js/js.dart" as js;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+import "package:js/js.dart";
+
+@#C4
+@#C5
+class StaticJSClass extends core::Object {
+  external constructor •() → sta::StaticJSClass
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → _in::JavaScriptObject
+    return new sta::StaticJSClass::•() as _in::JavaScriptObject;
+  static factory factory() → sta::StaticJSClass {
+    return new sta::StaticJSClass::•();
+  }
+  static method _#factory#tearOff() → _in::JavaScriptObject
+    return sta::StaticJSClass::factory|staticInteropFactoryStub();
+  static method /*isLegacy*/ factory|staticInteropFactoryStub() → _in::JavaScriptObject {
+    return (new sta::StaticJSClass::•() as _in::JavaScriptObject) as _in::JavaScriptObject;
+  }
+}
+@#C2
+external static method eval(core::String code) → void;
+static method setUp() → void {
+  sta::eval("function JSClass() {}");
+}
+
+constants  {
+  #C1 = null
+  #C2 = js::JS {name:#C1}
+  #C3 = "JSClass"
+  #C4 = js::JS {name:#C3}
+  #C5 = js::_StaticInterop {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:-1:-1)
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.transformed.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.transformed.expect
new file mode 100644
index 0000000..6bdb36d
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.strong.transformed.expect
@@ -0,0 +1,57 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "main_lib.dart" as sta;
+import "dart:_interceptors" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void {
+  sta::setUp();
+  _in::JavaScriptObject staticJs = sta::StaticJSClass::factory|staticInteropFactoryStub();
+}
+
+@#C2
+library static_interop /*isNonNullableByDefault*/;
+import self as sta;
+import "package:js/js.dart" as js;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+import "package:js/js.dart";
+
+@#C4
+@#C5
+class StaticJSClass extends core::Object {
+  external constructor •() → sta::StaticJSClass
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → _in::JavaScriptObject
+    return new sta::StaticJSClass::•() as _in::JavaScriptObject;
+  static factory factory() → sta::StaticJSClass {
+    return new sta::StaticJSClass::•();
+  }
+  static method _#factory#tearOff() → _in::JavaScriptObject
+    return sta::StaticJSClass::factory|staticInteropFactoryStub();
+  static method /*isLegacy*/ factory|staticInteropFactoryStub() → _in::JavaScriptObject {
+    return (new sta::StaticJSClass::•() as _in::JavaScriptObject) as _in::JavaScriptObject;
+  }
+}
+@#C2
+external static method eval(core::String code) → void;
+static method setUp() → void {
+  sta::eval("function JSClass() {}");
+}
+
+constants  {
+  #C1 = null
+  #C2 = js::JS {name:#C1}
+  #C3 = "JSClass"
+  #C4 = js::JS {name:#C3}
+  #C5 = js::_StaticInterop {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:-1:-1)
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline.expect
new file mode 100644
index 0000000..3e55fb8
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+import 'main_lib.dart';
+
+void main() {}
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3e55fb8
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+import 'main_lib.dart';
+
+void main() {}
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.expect
new file mode 100644
index 0000000..39fa210
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.expect
@@ -0,0 +1,56 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "main_lib.dart" as sta;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void {
+  sta::setUp();
+  sta::StaticJSClass staticJs = sta::StaticJSClass::factory();
+}
+
+@#C2
+library static_interop /*isNonNullableByDefault*/;
+import self as sta;
+import "package:js/js.dart" as js;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+import "package:js/js.dart";
+
+@#C4
+@#C5
+class StaticJSClass extends core::Object {
+  external constructor •() → sta::StaticJSClass
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → _in::JavaScriptObject
+    return new sta::StaticJSClass::•() as _in::JavaScriptObject;
+  static factory factory() → sta::StaticJSClass {
+    return new sta::StaticJSClass::•();
+  }
+  static method _#factory#tearOff() → _in::JavaScriptObject
+    return sta::StaticJSClass::factory|staticInteropFactoryStub();
+  static method /*isLegacy*/ factory|staticInteropFactoryStub() → _in::JavaScriptObject {
+    return (new sta::StaticJSClass::•() as _in::JavaScriptObject) as _in::JavaScriptObject;
+  }
+}
+@#C2
+external static method eval(core::String code) → void;
+static method setUp() → void {
+  sta::eval("function JSClass() {}");
+}
+
+constants  {
+  #C1 = null
+  #C2 = js::JS {name:#C1}
+  #C3 = "JSClass"
+  #C4 = js::JS {name:#C3}
+  #C5 = js::_StaticInterop {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:-1:-1)
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.modular.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.modular.expect
new file mode 100644
index 0000000..8d723b7
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.modular.expect
@@ -0,0 +1,10 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "main_lib.dart" as sta;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void {
+  sta::setUp();
+  sta::StaticJSClass staticJs = sta::StaticJSClass::factory();
+}
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.outline.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.outline.expect
new file mode 100644
index 0000000..4deaf52
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.outline.expect
@@ -0,0 +1,49 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void
+  ;
+
+@#C2
+library static_interop /*isNonNullableByDefault*/;
+import self as self2;
+import "package:js/js.dart" as js;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+import "package:js/js.dart";
+
+@#C4
+@#C5
+class StaticJSClass extends core::Object {
+  external constructor •() → self2::StaticJSClass
+    ;
+  static method _#new#tearOff() → _in::JavaScriptObject
+    return new self2::StaticJSClass::•() as _in::JavaScriptObject;
+  static factory factory() → self2::StaticJSClass
+    ;
+  static method _#factory#tearOff() → _in::JavaScriptObject
+    return self2::StaticJSClass::factory|staticInteropFactoryStub();
+  static method /*isLegacy*/ factory|staticInteropFactoryStub() → _in::JavaScriptObject
+    ;
+}
+@#C2
+external static method eval(core::String code) → void;
+static method setUp() → void
+  ;
+
+constants  {
+  #C1 = null
+  #C2 = js::JS {name:#C1}
+  #C3 = "JSClass"
+  #C4 = js::JS {name:#C3}
+  #C5 = js::_StaticInterop {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:-1:-1)
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.transformed.expect b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..6bdb36d
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main.dart.weak.transformed.expect
@@ -0,0 +1,57 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "main_lib.dart" as sta;
+import "dart:_interceptors" as _in;
+
+import "org-dartlang-testcase:///main_lib.dart";
+
+static method main() → void {
+  sta::setUp();
+  _in::JavaScriptObject staticJs = sta::StaticJSClass::factory|staticInteropFactoryStub();
+}
+
+@#C2
+library static_interop /*isNonNullableByDefault*/;
+import self as sta;
+import "package:js/js.dart" as js;
+import "dart:core" as core;
+import "dart:_interceptors" as _in;
+
+import "package:js/js.dart";
+
+@#C4
+@#C5
+class StaticJSClass extends core::Object {
+  external constructor •() → sta::StaticJSClass
+    : super core::Object::•()
+    ;
+  static method _#new#tearOff() → _in::JavaScriptObject
+    return new sta::StaticJSClass::•() as _in::JavaScriptObject;
+  static factory factory() → sta::StaticJSClass {
+    return new sta::StaticJSClass::•();
+  }
+  static method _#factory#tearOff() → _in::JavaScriptObject
+    return sta::StaticJSClass::factory|staticInteropFactoryStub();
+  static method /*isLegacy*/ factory|staticInteropFactoryStub() → _in::JavaScriptObject {
+    return (new sta::StaticJSClass::•() as _in::JavaScriptObject) as _in::JavaScriptObject;
+  }
+}
+@#C2
+external static method eval(core::String code) → void;
+static method setUp() → void {
+  sta::eval("function JSClass() {}");
+}
+
+constants  {
+  #C1 = null
+  #C2 = js::JS {name:#C1}
+  #C3 = "JSClass"
+  #C4 = js::JS {name:#C3}
+  #C5 = js::_StaticInterop {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///main_lib.dart:
+- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
+- Object. (from org-dartlang-sdk:///lib/core/object.dart:-1:-1)
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/main_lib.dart b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main_lib.dart
new file mode 100644
index 0000000..a5c93c4
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/main_lib.dart
@@ -0,0 +1,24 @@
+// 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.
+
+@JS()
+library static_interop;
+
+import 'package:js/js.dart';
+
+@JS()
+external void eval(String code);
+
+@JS('JSClass')
+@staticInterop
+class StaticJSClass {
+  external StaticJSClass();
+  factory StaticJSClass.factory() {
+    return StaticJSClass();
+  }
+}
+
+void setUp() {
+  eval('''function JSClass() {}''');
+}
diff --git a/pkg/front_end/testcases/dartdevc/static_interop_erasure/test.options b/pkg/front_end/testcases/dartdevc/static_interop_erasure/test.options
new file mode 100644
index 0000000..8bf09cc
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/static_interop_erasure/test.options
@@ -0,0 +1,4 @@
+main_lib.dart
+package:js/js.dart
+package:meta/meta.dart
+package:meta/meta_meta.dart
\ No newline at end of file
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index ad3c6bd..4c79f85 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -227,6 +227,10 @@
   void _relinkNode() {
     this.reference.node = this;
   }
+
+  /// Computes the canonical names for this node using the [parent] as the
+  /// canonical name of the parent node.
+  void bindCanonicalNames(CanonicalName parent);
 }
 
 abstract class FileUriNode extends TreeNode {
@@ -489,36 +493,28 @@
     typedefs.add(typedef_);
   }
 
-  void computeCanonicalNames() {
-    CanonicalName canonicalName = this.reference.canonicalName!;
+  @override
+  CanonicalName bindCanonicalNames(CanonicalName parent) {
+    return parent.getChildFromUri(importUri)..bindTo(reference);
+  }
+
+  /// Computes the canonical name for this library and all its members.
+  void ensureCanonicalNames(CanonicalName parent) {
+    CanonicalName canonicalName = bindCanonicalNames(parent);
     for (int i = 0; i < typedefs.length; ++i) {
-      Typedef typedef_ = typedefs[i];
-      canonicalName.getChildFromTypedef(typedef_).bindTo(typedef_.reference);
+      typedefs[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < fields.length; ++i) {
-      Field field = fields[i];
-      canonicalName.getChildFromField(field).bindTo(field.fieldReference);
-      canonicalName
-          .getChildFromFieldGetter(field)
-          .bindTo(field.getterReference);
-      if (field.hasSetter) {
-        canonicalName
-            .getChildFromFieldSetter(field)
-            .bindTo(field.setterReference!);
-      }
+      fields[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < procedures.length; ++i) {
-      Procedure member = procedures[i];
-      canonicalName.getChildFromProcedure(member).bindTo(member.reference);
+      procedures[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < classes.length; ++i) {
-      Class class_ = classes[i];
-      canonicalName.getChild(class_.name).bindTo(class_.reference);
-      class_.computeCanonicalNames();
+      classes[i].ensureCanonicalNames(canonicalName);
     }
     for (int i = 0; i < extensions.length; ++i) {
-      Extension extension = extensions[i];
-      canonicalName.getChild(extension.name).bindTo(extension.reference);
+      extensions[i].bindCanonicalNames(canonicalName);
     }
   }
 
@@ -854,6 +850,11 @@
     setParents(this.typeParameters, this);
   }
 
+  @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChildFromTypedef(this).bindTo(reference);
+  }
+
   Library get enclosingLibrary => parent as Library;
 
   @override
@@ -1247,34 +1248,26 @@
     this.isAnonymousMixin = isAnonymousMixin;
   }
 
-  void computeCanonicalNames() {
-    CanonicalName canonicalName = this.reference.canonicalName!;
+  @override
+  CanonicalName bindCanonicalNames(CanonicalName parent) {
+    return parent.getChild(name)..bindTo(reference);
+  }
+
+  /// Computes the canonical name for this class and all its members.
+  void ensureCanonicalNames(CanonicalName parent) {
+    CanonicalName canonicalName = bindCanonicalNames(parent);
     if (!dirty) return;
     for (int i = 0; i < fields.length; ++i) {
-      Field member = fields[i];
-      canonicalName.getChildFromField(member).bindTo(member.fieldReference);
-      canonicalName
-          .getChildFromFieldGetter(member)
-          .bindTo(member.getterReference);
-      if (member.hasSetter) {
-        canonicalName
-            .getChildFromFieldSetter(member)
-            .bindTo(member.setterReference!);
-      }
+      fields[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < procedures.length; ++i) {
-      Procedure member = procedures[i];
-      canonicalName.getChildFromProcedure(member).bindTo(member.reference);
+      procedures[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < constructors.length; ++i) {
-      Constructor member = constructors[i];
-      canonicalName.getChildFromConstructor(member).bindTo(member.reference);
+      constructors[i].bindCanonicalNames(canonicalName);
     }
     for (int i = 0; i < redirectingFactories.length; ++i) {
-      RedirectingFactory member = redirectingFactories[i];
-      canonicalName
-          .getChildFromRedirectingFactory(member)
-          .bindTo(member.reference);
+      redirectingFactories[i].bindCanonicalNames(canonicalName);
     }
     dirty = false;
   }
@@ -1576,6 +1569,11 @@
     }
   }
 
+  @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChild(name).bindTo(reference);
+  }
+
   Library get enclosingLibrary => parent as Library;
 
   bool get isExtensionTypeDeclaration {
@@ -2193,6 +2191,15 @@
   }
 
   @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChildFromField(this).bindTo(fieldReference);
+    parent.getChildFromFieldGetter(this).bindTo(getterReference);
+    if (hasSetter) {
+      parent.getChildFromFieldSetter(this).bindTo(setterReference!);
+    }
+  }
+
+  @override
   void _relinkNode() {
     this.fieldReference.node = this;
     this.getterReference.node = this;
@@ -2407,6 +2414,11 @@
   }
 
   @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChildFromConstructor(this).bindTo(reference);
+  }
+
+  @override
   Class get enclosingClass => parent as Class;
 
   static const int FlagConst = 1 << 0; // Must match serialized bit positions.
@@ -2574,6 +2586,11 @@
   }
 
   @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChildFromRedirectingFactory(this).bindTo(reference);
+  }
+
+  @override
   Class get enclosingClass => parent as Class;
 
   static const int FlagConst = 1 << 0; // Must match serialized bit positions.
@@ -3002,6 +3019,11 @@
         "$memberSignatureOrigin for $this.");
   }
 
+  @override
+  void bindCanonicalNames(CanonicalName parent) {
+    parent.getChildFromProcedure(this).bindTo(reference);
+  }
+
   static const int FlagStatic = 1 << 0; // Must match serialized bit positions.
   static const int FlagAbstract = 1 << 1;
   static const int FlagExternal = 1 << 2;
@@ -11429,9 +11451,7 @@
   /// list is omitted, 'dynamic' type arguments are filled in.
   InterfaceType(Class classNode, Nullability declaredNullability,
       [List<DartType>? typeArguments])
-      : this.byReference(
-            getNonNullableClassReference(classNode),
-            declaredNullability,
+      : this.byReference(classNode.reference, declaredNullability,
             typeArguments ?? _defaultTypeArguments(classNode));
 
   InterfaceType.byReference(
@@ -11733,9 +11753,9 @@
   final Reference typedefReference;
   final List<DartType> typeArguments;
 
-  TypedefType(Typedef typedefNode, Nullability nullability,
+  TypedefType(Typedef typedef, Nullability nullability,
       [List<DartType>? typeArguments])
-      : this.byReference(typedefNode.reference, nullability,
+      : this.byReference(typedef.reference, nullability,
             typeArguments ?? const <DartType>[]);
 
   TypedefType.byReference(
@@ -12807,8 +12827,7 @@
   final List<DartType> typeArguments;
 
   Supertype(Class classNode, List<DartType> typeArguments)
-      : this.byReference(
-            getNonNullableClassReference(classNode), typeArguments);
+      : this.byReference(classNode.reference, typeArguments);
 
   Supertype.byReference(this.className, this.typeArguments);
 
@@ -13992,8 +14011,7 @@
   }
 
   void computeCanonicalNamesForLibrary(Library library) {
-    root.getChildFromUri(library.importUri).bindTo(library.reference);
-    library.computeCanonicalNames();
+    library.ensureCanonicalNames(root);
   }
 
   void unbindCanonicalNames() {
@@ -14142,7 +14160,7 @@
   /// Write List<Byte> into the sink.
   void writeByteList(List<int> bytes);
 
-  void writeNullAllowedCanonicalNameReference(CanonicalName? name);
+  void writeNullAllowedCanonicalNameReference(Reference? reference);
   void writeStringReference(String str);
   void writeName(Name node);
   void writeDartType(DartType type);
@@ -14348,75 +14366,6 @@
   return member.reference;
 }
 
-/// Returns the [Reference] object for the given class.
-///
-/// Returns `null` if the class is `null`.
-Reference? getClassReference(Class? class_) {
-  return class_?.reference;
-}
-
-/// Returns the [Reference] object for the given class.
-Reference getNonNullableClassReference(Class class_) {
-  return class_.reference;
-}
-
-/// Returns the canonical name of [member], or throws an exception if the
-/// member has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfMemberGetter(Member member) {
-  CanonicalName? canonicalName;
-  if (member is Field) {
-    canonicalName = member.getterReference.canonicalName;
-  } else {
-    canonicalName = member.reference.canonicalName;
-  }
-  if (canonicalName == null) {
-    throw '$member has no canonical name';
-  }
-  return canonicalName;
-}
-
-/// Returns the canonical name of [member], or throws an exception if the
-/// member has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfMemberSetter(Member member) {
-  CanonicalName? canonicalName;
-  if (member is Field) {
-    canonicalName = member.setterReference!.canonicalName;
-  } else {
-    canonicalName = member.reference.canonicalName;
-  }
-  if (canonicalName == null) {
-    throw '$member has no canonical name';
-  }
-  return canonicalName;
-}
-
-/// Returns the canonical name of [class_], or throws an exception if the
-/// class has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfClass(Class class_) {
-  if (class_.reference.canonicalName == null) {
-    throw '$class_ has no canonical name';
-  }
-  return class_.reference.canonicalName!;
-}
-
-/// Returns the canonical name of [extension], or throws an exception if the
-/// class has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfExtension(Extension extension) {
-  if (extension.reference.canonicalName == null) {
-    throw '$extension has no canonical name';
-  }
-  return extension.reference.canonicalName!;
-}
-
-/// Returns the canonical name of [library], or throws an exception if the
-/// library has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfLibrary(Library library) {
-  if (library.reference.canonicalName == null) {
-    throw '$library has no canonical name';
-  }
-  return library.reference.canonicalName!;
-}
-
 /// Murmur-inspired hashing, with a fall-back to Jenkins-inspired hashing when
 /// compiled to JavaScript.
 ///
@@ -14548,15 +14497,6 @@
   return true;
 }
 
-/// Returns the canonical name of [typedef_], or throws an exception if the
-/// typedef has not been assigned a canonical name yet.
-CanonicalName getCanonicalNameOfTypedef(Typedef typedef_) {
-  if (typedef_.reference.canonicalName == null) {
-    throw '$typedef_ has no canonical name';
-  }
-  return typedef_.reference.canonicalName!;
-}
-
 /// Annotation describing information which is not part of Dart semantics; in
 /// other words, if this information (or any information it refers to) changes,
 /// static analysis and runtime behavior of the library are unaffected.
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 7c9ac33..4d8d015 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -258,7 +258,7 @@
       constant.typeArguments.forEach(writeDartType);
       writeUInt30(constant.fieldValues.length);
       constant.fieldValues.forEach((Reference fieldRef, Constant value) {
-        writeNonNullCanonicalNameReference(fieldRef.canonicalName!);
+        writeNonNullCanonicalNameReference(fieldRef);
         writeConstantReference(value);
       });
     } else if (constant is InstantiationConstant) {
@@ -271,16 +271,13 @@
       }
     } else if (constant is StaticTearOffConstant) {
       writeByte(ConstantTag.StaticTearOffConstant);
-      writeNonNullCanonicalNameReference(
-          constant.targetReference.canonicalName!);
+      writeNonNullCanonicalNameReference(constant.targetReference);
     } else if (constant is ConstructorTearOffConstant) {
       writeByte(ConstantTag.ConstructorTearOffConstant);
-      writeNonNullCanonicalNameReference(
-          constant.targetReference.canonicalName!);
+      writeNonNullCanonicalNameReference(constant.targetReference);
     } else if (constant is RedirectingFactoryTearOffConstant) {
       writeByte(ConstantTag.RedirectingFactoryTearOffConstant);
-      writeNonNullCanonicalNameReference(
-          constant.targetReference.canonicalName!);
+      writeNonNullCanonicalNameReference(constant.targetReference);
     } else if (constant is TypeLiteralConstant) {
       writeByte(ConstantTag.TypeLiteralConstant);
       writeDartType(constant.type);
@@ -553,7 +550,7 @@
   }
 
   /// Compute canonical names for the whole component or parts of it.
-  void computeCanonicalNames(Component component) {
+  void _computeCanonicalNames(Component component) {
     for (int i = 0; i < component.libraries.length; ++i) {
       Library library = component.libraries[i];
       if (libraryFilter == null || libraryFilter!(library)) {
@@ -576,7 +573,7 @@
   void writeComponentFile(Component component) {
     Timeline.timeSync("BinaryPrinter.writeComponentFile", () {
       compilationMode = component.mode;
-      computeCanonicalNames(component);
+      _computeCanonicalNames(component);
       final int componentOffset = getBufferOffset();
       writeUInt32(Tag.ComponentFile);
       writeUInt32(Tag.BinaryFormatVersion);
@@ -590,7 +587,8 @@
       libraryOffsets = <int>[];
       Procedure? mainMethod = component.mainMethod;
       if (mainMethod != null) {
-        checkCanonicalName(getCanonicalNameOfMemberGetter(mainMethod));
+        checkCanonicalName(_ensureCanonicalName(
+            getNonNullableMemberReferenceGetter(mainMethod)));
       }
       writeLibraries(component);
       writeUriToSource(component.uriToSource);
@@ -801,7 +799,8 @@
     if (mainMethod == null) {
       writeUInt32(0);
     } else {
-      CanonicalName main = getCanonicalNameOfMemberGetter(mainMethod);
+      CanonicalName main =
+          _ensureCanonicalName(getNonNullableMemberReferenceGetter(mainMethod));
       writeUInt32(main.index + 1);
     }
     assert(component.modeRaw != null, "Component mode not set.");
@@ -915,10 +914,7 @@
       writeUInt30(0);
     } else {
       assert(reference.isConsistent, reference.getInconsistency());
-      CanonicalName? name = reference.canonicalName;
-      if (name == null) {
-        throw new ArgumentError('Missing canonical name for $reference');
-      }
+      CanonicalName name = _ensureCanonicalName(reference);
       checkCanonicalName(name);
       writeUInt30(name.index + 1);
     }
@@ -936,15 +932,51 @@
       throw new ArgumentError('Got null reference');
     } else {
       assert(reference.isConsistent, reference.getInconsistency());
-      CanonicalName? name = reference.canonicalName;
-      if (name == null) {
-        throw new ArgumentError('Missing canonical name for $reference');
-      }
+      CanonicalName name = _ensureCanonicalName(reference);
       checkCanonicalName(name);
       writeUInt30(name.index + 1);
     }
   }
 
+  /// Returns the canonical name for [reference].
+  ///
+  /// If the canonical name has already been computed it is returned. Otherwise
+  /// canonical names for the reference node and all of its parent nodes are
+  /// created. If the reference doesn't have a node or the node is not part of
+  /// a component, an error is thrown.
+  ///
+  /// This should not be used for reference of the member of the serialized
+  /// libraries, for instance for `Library.reference` in [visitLibrary], since
+  /// the canonical names of these references should already have been computed
+  /// through [_computeCanonicalNames].
+  CanonicalName _ensureCanonicalName(Reference reference) {
+    CanonicalName? canonicalName = reference.canonicalName;
+    if (canonicalName != null) {
+      return canonicalName;
+    }
+
+    CanonicalName ensureCanonicalNameForNode(TreeNode? parentNode) {
+      if (parentNode is Component) {
+        return parentNode.root;
+      }
+      if (parentNode is NamedNode) {
+        CanonicalName? canonicalName = parentNode.reference.canonicalName;
+        if (canonicalName != null) {
+          return canonicalName;
+        }
+        CanonicalName parentCanonicalName =
+            ensureCanonicalNameForNode(parentNode.parent);
+        parentNode.bindCanonicalNames(parentCanonicalName);
+        return parentNode.reference.canonicalName!;
+      } else {
+        throw new ArgumentError('Missing canonical name for $reference');
+      }
+    }
+
+    ensureCanonicalNameForNode(reference.node);
+    return reference.canonicalName!;
+  }
+
   void checkCanonicalName(CanonicalName node) {
     if (_knownCanonicalNameNonRootTops.contains(node.nonRootTop)) return;
     if (node.isRoot) return;
@@ -961,17 +993,31 @@
     _canonicalNameList.add(node);
   }
 
-  @override
-  void writeNullAllowedCanonicalNameReference(CanonicalName? name) {
-    if (name == null) {
+  void _writeNullAllowedCanonicalName(CanonicalName? canonicalName) {
+    if (canonicalName == null) {
       writeUInt30(0);
     } else {
-      checkCanonicalName(name);
-      writeUInt30(name.index + 1);
+      _writeNonNullCanonicalName(canonicalName);
     }
   }
 
-  void writeNonNullCanonicalNameReference(CanonicalName name) {
+  @override
+  void writeNullAllowedCanonicalNameReference(Reference? reference) {
+    if (reference == null) {
+      writeUInt30(0);
+    } else {
+      CanonicalName name = _ensureCanonicalName(reference);
+      _writeNonNullCanonicalName(name);
+    }
+  }
+
+  void _writeNonNullCanonicalName(CanonicalName canonicalName) {
+    checkCanonicalName(canonicalName);
+    writeUInt30(canonicalName.index + 1);
+  }
+
+  void writeNonNullCanonicalNameReference(Reference reference) {
+    CanonicalName name = _ensureCanonicalName(reference);
     // ignore: unnecessary_null_comparison
     if (name == null) {
       throw new ArgumentError(
@@ -982,14 +1028,6 @@
     }
   }
 
-  void writeLibraryReference(Library node, {bool allowNull: false}) {
-    if (node.reference.canonicalName == null && !allowNull) {
-      throw new ArgumentError(
-          'Expected a library reference to be valid but was `null`.');
-    }
-    writeNullAllowedCanonicalNameReference(node.reference.canonicalName);
-  }
-
   void writeOffset(int offset) {
     // TODO(jensj): Delta-encoding.
     // File offset ranges from -1 and up,
@@ -1007,7 +1045,7 @@
       throw new ArgumentError(
           'Expected a class reference to be valid but was `null`.');
     }
-    writeNonNullCanonicalNameReference(getCanonicalNameOfClass(class_));
+    writeNonNullCanonicalNameReference(class_.reference);
   }
 
   @override
@@ -1019,7 +1057,7 @@
     // TODO: Consider a more compressed format for private names within the
     // enclosing library.
     if (node.isPrivate) {
-      writeLibraryReference(node.library!);
+      writeNonNullCanonicalNameReference(node.library!.reference);
     }
   }
 
@@ -1042,7 +1080,11 @@
     writeUInt30(node.languageVersion.major);
     writeUInt30(node.languageVersion.minor);
 
-    writeNonNullCanonicalNameReference(getCanonicalNameOfLibrary(node));
+    CanonicalName? canonicalName = node.reference.canonicalName;
+    if (canonicalName == null) {
+      throw new ArgumentError('Missing canonical name for $node');
+    }
+    _writeNonNullCanonicalName(canonicalName);
     writeStringReference(node.name ?? '');
     writeUriReference(node.fileUri);
     writeListOfStrings(node.problemsAsJson);
@@ -1126,7 +1168,7 @@
     writeOffset(node.fileOffset);
     writeByte(node.flags);
     writeAnnotationList(node.annotations);
-    writeLibraryReference(node.targetLibrary, allowNull: false);
+    writeNonNullCanonicalNameReference(node.targetLibrary.reference);
     writeStringReference(node.name ?? '');
     writeNodeList(node.combinators);
   }
@@ -1155,8 +1197,12 @@
 
   @override
   void visitTypedef(Typedef node) {
+    CanonicalName? canonicalName = node.reference.canonicalName;
+    if (canonicalName == null) {
+      throw new ArgumentError('Missing canonical name for $node');
+    }
     enterScope(memberScope: true);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfTypedef(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.fileOffset);
     writeStringReference(node.name);
@@ -1189,11 +1235,12 @@
 
     if (node.isAnonymousMixin) _currentlyInNonimplementation = true;
 
-    if (node.reference.canonicalName == null) {
+    CanonicalName? canonicalName = node.reference.canonicalName;
+    if (canonicalName == null) {
       throw new ArgumentError('Missing canonical name for $node');
     }
     writeByte(Tag.Class);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfClass(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.startFileOffset);
     writeOffset(node.fileOffset);
@@ -1229,12 +1276,14 @@
 
   @override
   void visitConstructor(Constructor node) {
-    if (node.reference.canonicalName == null) {
+    CanonicalName? canonicalName =
+        getNonNullableMemberReferenceGetter(node).canonicalName;
+    if (canonicalName == null) {
       throw new ArgumentError('Missing canonical name for $node');
     }
     enterScope(memberScope: true);
     writeByte(Tag.Constructor);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.startFileOffset);
     writeOffset(node.fileOffset);
@@ -1297,7 +1346,7 @@
 
     enterScope(memberScope: true);
     writeByte(Tag.Procedure);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.startFileOffset);
     writeOffset(node.fileOffset);
@@ -1380,9 +1429,9 @@
     }
     enterScope(memberScope: true);
     writeByte(Tag.Field);
-    writeNonNullCanonicalNameReference(fieldCanonicalName);
-    writeNonNullCanonicalNameReference(getterCanonicalName);
-    writeNullAllowedCanonicalNameReference(setterCanonicalName);
+    _writeNonNullCanonicalName(fieldCanonicalName);
+    _writeNonNullCanonicalName(getterCanonicalName);
+    _writeNullAllowedCanonicalName(setterCanonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.fileOffset);
     writeOffset(node.fileEndOffset);
@@ -1396,11 +1445,13 @@
 
   @override
   void visitRedirectingFactory(RedirectingFactory node) {
-    if (node.reference.canonicalName == null) {
+    CanonicalName? canonicalName =
+        getNonNullableMemberReferenceGetter(node).canonicalName;
+    if (canonicalName == null) {
       throw new ArgumentError('Missing canonical name for $node');
     }
     writeByte(Tag.RedirectingFactory);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfMemberGetter(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeUriReference(node.fileUri);
     writeOffset(node.fileOffset);
     writeOffset(node.fileEndOffset);
@@ -2477,11 +2528,12 @@
 
   @override
   void visitExtension(Extension node) {
-    if (node.reference.canonicalName == null) {
+    CanonicalName? canonicalName = node.reference.canonicalName;
+    if (canonicalName == null) {
       throw new ArgumentError('Missing canonical name for $node');
     }
     writeByte(Tag.Extension);
-    writeNonNullCanonicalNameReference(getCanonicalNameOfExtension(node));
+    _writeNonNullCanonicalName(canonicalName);
     writeStringReference(node.name);
     writeAnnotationList(node.annotations);
     writeUriReference(node.fileUri);
@@ -2520,7 +2572,7 @@
       writeByte(descriptor.flags);
       assert(descriptor.member.canonicalName != null,
           "No canonical name for ${descriptor}.");
-      writeNonNullCanonicalNameReference(descriptor.member.canonicalName!);
+      writeNonNullCanonicalNameReference(descriptor.member);
     }
   }
 
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 707efa0..9c46dbb 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -1091,10 +1091,7 @@
         // be computed as part of serialization, so we need to do that
         // preemptively here to avoid errors when serializing references to
         // elements of these libraries.
-        component.root
-            .getChildFromUri(library.importUri)
-            .bindTo(library.reference);
-        library.computeCanonicalNames();
+        library.bindCanonicalNames(component.root);
       }
     }
   }
diff --git a/pkg/kernel/test/metadata_test.dart b/pkg/kernel/test/metadata_test.dart
index 3f53712..2554597 100644
--- a/pkg/kernel/test/metadata_test.dart
+++ b/pkg/kernel/test/metadata_test.dart
@@ -68,8 +68,7 @@
     expect(metadata, equals(mapping[node]));
     sink.writeByteList(utf8.encode(metadata.string));
     sink.writeStringReference(metadata.string);
-    sink.writeNullAllowedCanonicalNameReference(
-        metadata.member?.reference.canonicalName);
+    sink.writeNullAllowedCanonicalNameReference(metadata.member?.reference);
     sink.writeDartType(metadata.type);
   }
 
diff --git a/pkg/vm/lib/metadata/direct_call.dart b/pkg/vm/lib/metadata/direct_call.dart
index 3539978..f6b37df 100644
--- a/pkg/vm/lib/metadata/direct_call.dart
+++ b/pkg/vm/lib/metadata/direct_call.dart
@@ -41,7 +41,7 @@
   @override
   void writeToBinary(DirectCallMetadata metadata, Node node, BinarySink sink) {
     sink.writeNullAllowedCanonicalNameReference(
-        getCanonicalNameOfMemberGetter(metadata.target));
+        getMemberReferenceGetter(metadata.target));
     sink.writeByte(metadata.checkReceiverForNull ? 1 : 0);
   }
 
diff --git a/pkg/vm/lib/metadata/inferred_type.dart b/pkg/vm/lib/metadata/inferred_type.dart
index 6f23f6e..2432195 100644
--- a/pkg/vm/lib/metadata/inferred_type.dart
+++ b/pkg/vm/lib/metadata/inferred_type.dart
@@ -39,7 +39,7 @@
       bool skipCheck: false,
       bool receiverNotInt: false})
       : this._byReference(
-            getClassReference(concreteClass),
+            concreteClass?.reference,
             constantValue,
             (nullable ? flagNullable : 0) |
                 (isInt ? flagInt : 0) |
@@ -114,9 +114,8 @@
   void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
     // TODO(sjindel/tfa): Implement serialization of type arguments when can use
     // them for optimizations.
-    sink.writeNullAllowedCanonicalNameReference(metadata.concreteClass != null
-        ? getCanonicalNameOfClass(metadata.concreteClass!)
-        : null);
+    sink.writeNullAllowedCanonicalNameReference(
+        metadata.concreteClass?.reference);
     sink.writeByte(metadata._flags);
     if (metadata.constantValue != null) {
       sink.writeConstantReference(metadata.constantValue!);
diff --git a/runtime/tests/vm/dart/regress_49279_test.dart b/runtime/tests/vm/dart/regress_49279_test.dart
index 78a3c34..7853d4f 100644
--- a/runtime/tests/vm/dart/regress_49279_test.dart
+++ b/runtime/tests/vm/dart/regress_49279_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-// VMOptions=--null-assertions --no-sound-null-safety --enable-asserts
+// VMOptions=--null-assertions --enable-asserts
 
 void main() {
   try {
diff --git a/runtime/tests/vm/dart_2/regress_49279_test.dart b/runtime/tests/vm/dart_2/regress_49279_test.dart
index 78a3c34..7853d4f 100644
--- a/runtime/tests/vm/dart_2/regress_49279_test.dart
+++ b/runtime/tests/vm/dart_2/regress_49279_test.dart
@@ -2,7 +2,7 @@
 // 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.
 
-// VMOptions=--null-assertions --no-sound-null-safety --enable-asserts
+// VMOptions=--null-assertions --enable-asserts
 
 void main() {
   try {
diff --git a/tools/VERSION b/tools/VERSION
index 51d957b..9f203d5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 201
+PRERELEASE 202
 PRERELEASE_PATCH 0
\ No newline at end of file