Version 2.18.0-92.0.dev

Merge commit 'b488bd1a75940c43ee236bfe05b562b42c63eb05' into 'dev'
diff --git a/pkg/analyzer/test/src/dart/resolution/comment_test.dart b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
index f3e4408..dba14af 100644
--- a/pkg/analyzer/test/src/dart/resolution/comment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/comment_test.dart
@@ -31,8 +31,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.named]'), findElement.class_('A'));
-    assertElement(findNode.simple('named]'), findElement.constructor('named'));
+    assertResolvedNodeText(findNode.commentReference('A.named]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: named
+      staticElement: self::@class::A::@constructor::named
+      staticType: null
+    staticElement: self::@class::A::@constructor::named
+    staticType: null
+''');
   }
 
   test_class_constructor_unnamedViaNew() async {
@@ -45,8 +58,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.new'), findElement.class_('A'));
-    assertElement(findNode.simple('new]'), findElement.unnamedConstructor('A'));
+    assertResolvedNodeText(findNode.commentReference('A.new]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: new
+      staticElement: self::@class::A::@constructor::•
+      staticType: null
+    staticElement: self::@class::A::@constructor::•
+    staticType: null
+''');
   }
 
   test_class_instanceGetter() async {
@@ -59,8 +85,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@getter::foo
+      staticType: null
+    staticElement: self::@class::A::@getter::foo
+    staticType: null
+''');
   }
 
   test_class_instanceMethod() async {
@@ -73,8 +112,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@method::foo
+      staticType: null
+    staticElement: self::@class::A::@method::foo
+    staticType: null
+''');
   }
 
   test_class_instanceSetter() async {
@@ -87,8 +139,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@setter::foo
+      staticType: null
+    staticElement: self::@class::A::@setter::foo
+    staticType: null
+''');
   }
 
   test_class_invalid_ambiguousExtension() async {
@@ -139,8 +204,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@getter::foo
+      staticType: null
+    staticElement: self::@class::A::@getter::foo
+    staticType: null
+''');
   }
 
   test_class_staticMethod() async {
@@ -153,8 +231,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@method::foo
+      staticType: null
+    staticElement: self::@class::A::@method::foo
+    staticType: null
+''');
   }
 
   test_class_staticSetter() async {
@@ -167,8 +258,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@setter::foo
+      staticType: null
+    staticElement: self::@class::A::@setter::foo
+    staticType: null
+''');
   }
 
   test_extension_instanceGetter() async {
@@ -181,8 +285,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@getter::foo
+      staticType: null
+    staticElement: self::@extension::E::@getter::foo
+    staticType: null
+''');
   }
 
   test_extension_instanceMethod() async {
@@ -195,8 +312,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@method::foo
+      staticType: null
+    staticElement: self::@extension::E::@method::foo
+    staticType: null
+''');
   }
 
   test_extension_instanceSetter() async {
@@ -209,8 +339,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@setter::foo
+      staticType: null
+    staticElement: self::@extension::E::@setter::foo
+    staticType: null
+''');
   }
 
   test_extension_staticGetter() async {
@@ -223,8 +366,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@getter::foo
+      staticType: null
+    staticElement: self::@extension::E::@getter::foo
+    staticType: null
+''');
   }
 
   test_extension_staticMethod() async {
@@ -237,8 +393,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@method::foo
+      staticType: null
+    staticElement: self::@extension::E::@method::foo
+    staticType: null
+''');
   }
 
   test_extension_staticSetter() async {
@@ -251,8 +420,21 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: E
+      staticElement: self::@extension::E
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@setter::foo
+      staticType: null
+    staticElement: self::@extension::E::@setter::foo
+    staticType: null
+''');
   }
 }
 
@@ -270,10 +452,29 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.named'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.named]'), findElement.class_('A'));
     // TODO(srawlins): Set the type of named, and test it, here and below.
-    assertElement(findNode.simple('named]'), findElement.constructor('named'));
+    assertResolvedNodeText(findNode.commentReference('A.named]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: named
+      staticElement: self::@class::A::@constructor::named
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_constructor_unnamedViaNew() async {
@@ -287,9 +488,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.new'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.new'), findElement.class_('A'));
-    assertElement(findNode.simple('new]'), findElement.unnamedConstructor('A'));
+    assertResolvedNodeText(findNode.commentReference('A.new]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: new
+      staticElement: self::@class::A::@constructor::•
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_instanceGetter() async {
@@ -303,9 +523,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@getter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_instanceMethod() async {
@@ -319,9 +558,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@method::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_instanceSetter() async {
@@ -335,9 +593,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@setter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_staticGetter() async {
@@ -351,9 +628,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@getter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_staticMethod() async {
@@ -367,9 +663,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@method::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_class_staticSetter() async {
@@ -383,9 +698,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.A.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('A.foo'), findElement.class_('A'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('A.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: A
+        staticElement: self::@class::A
+        staticType: null
+      staticElement: self::@class::A
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@class::A::@setter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_instanceGetter() async {
@@ -399,9 +733,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@getter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_instanceMethod() async {
@@ -415,9 +768,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@method::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_instanceSetter() async {
@@ -431,9 +803,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@setter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_staticGetter() async {
@@ -447,9 +838,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.getter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@getter::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_staticMethod() async {
@@ -463,9 +873,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.method('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@method::foo
+      staticType: null
+    staticType: null
+''');
   }
 
   test_extension_staticSetter() async {
@@ -479,9 +908,28 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('self.E.foo'), findElement.prefix('self'));
-    assertElement(findNode.simple('E.foo'), findElement.extension_('E'));
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('E.foo]'), r'''
+CommentReference
+  expression: PropertyAccess
+    target: PrefixedIdentifier
+      prefix: SimpleIdentifier
+        token: self
+        staticElement: self::@prefix::self
+        staticType: null
+      period: .
+      identifier: SimpleIdentifier
+        token: E
+        staticElement: self::@extension::E
+        staticType: null
+      staticElement: self::@extension::E
+      staticType: null
+    operator: .
+    propertyName: SimpleIdentifier
+      token: foo
+      staticElement: self::@extension::E::@setter::foo
+      staticType: null
+    staticType: null
+''');
   }
 }
 
@@ -498,7 +946,13 @@
 void f() {}
 ''');
 
-    assertElement(findNode.simple('foo]'), findElement.topGet('foo'));
+    assertResolvedNodeText(findNode.commentReference('foo]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: foo
+    staticElement: self::@getter::foo
+    staticType: null
+''');
   }
 
   test_associatedSetterAndGetter_setterInScope() async {
@@ -513,7 +967,13 @@
 }
 ''');
 
-    assertElement(findNode.simple('foo]'), findElement.setter('foo'));
+    assertResolvedNodeText(findNode.commentReference('foo]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: foo
+    staticElement: self::@extension::E2::@setter::foo
+    staticType: null
+''');
   }
 
   test_beforeClass() async {
@@ -524,10 +984,13 @@
 }
 ''');
 
-    assertElement(
-      findNode.simple('foo]'),
-      findElement.method('foo'),
-    );
+    assertResolvedNodeText(findNode.commentReference('foo]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: foo
+    staticElement: self::@class::A::@method::foo
+    staticType: null
+''');
   }
 
   test_beforeConstructor() async {
@@ -537,10 +1000,13 @@
   A(int p);
 }''');
 
-    assertElement(
-      findNode.simple('p]'),
-      findElement.parameter('p'),
-    );
+    assertResolvedNodeText(findNode.commentReference('p]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p
+    staticElement: p@28
+    staticType: null
+''');
   }
 
   test_beforeEnum() async {
@@ -553,18 +1019,29 @@
   WITHOUT_SWORD
 }''');
 
-    assertElement(
-      findNode.simple('Samurai]'),
-      findElement.enum_('Samurai'),
-    );
-    assertElement(
-      findNode.simple('int]'),
-      intElement,
-    );
-    assertElement(
-      findNode.simple('WITH_SWORD]'),
-      findElement.getter('WITH_SWORD'),
-    );
+    assertResolvedNodeText(findNode.commentReference('Samurai]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: Samurai
+    staticElement: self::@enum::Samurai
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('int]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: int
+    staticElement: dart:core::@class::int
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('WITH_SWORD]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: WITH_SWORD
+    staticElement: self::@enum::Samurai::@getter::WITH_SWORD
+    staticType: null
+''');
   }
 
   test_beforeFunction_blockBody() async {
@@ -585,10 +1062,13 @@
 foo(int p) => null;
 ''');
 
-    assertElement(
-      findNode.simple('p]'),
-      findElement.parameter('p'),
-    );
+    assertResolvedNodeText(findNode.commentReference('p]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p
+    staticElement: p@16
+    staticType: null
+''');
   }
 
   test_beforeFunctionTypeAlias() async {
@@ -597,10 +1077,13 @@
 typedef Foo(int p);
 ''');
 
-    assertElement(
-      findNode.simple('p]'),
-      findElement.parameter('p'),
-    );
+    assertResolvedNodeText(findNode.commentReference('p]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p
+    staticElement: p@24
+    staticType: null
+''');
   }
 
   test_beforeGenericTypeAlias() async {
@@ -609,15 +1092,29 @@
 typedef Foo<T> = Function<S>(int p);
 ''');
 
-    assertElement(
-      findNode.simple('T]'),
-      findElement.typeParameter('T'),
-    );
-    assertElement(findNode.simple('S]'), findElement.typeParameter('S'));
-    assertElement(
-      findNode.simple('p]'),
-      findElement.parameter('p'),
-    );
+    assertResolvedNodeText(findNode.commentReference('T]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: T
+    staticElement: T@47
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('S]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: S
+    staticElement: S@61
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('p]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p
+    staticElement: p@68
+    staticType: null
+''');
   }
 
   test_beforeGetter() async {
@@ -646,11 +1143,45 @@
 }
 ''');
 
-    assertElement(findNode.simple('p1]'), findElement.parameter('p1'));
-    assertElement(findNode.simple('p2]'), findElement.parameter('p2'));
-    assertElement(findNode.simple('p3]'), findElement.parameter('p3'));
-    assertElement(findNode.simple('p4]'), findElement.parameter('p4'));
-    assertElement(findNode.simple('p5]'), findElement.parameter('p5'));
+    assertResolvedNodeText(findNode.commentReference('p1]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p1
+    staticElement: p1@39
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('p2]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p2
+    staticElement: p2@65
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('p3]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p3
+    staticElement: p3@100
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('p4]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p4
+    staticElement: self::@class::A::@method::mc::@parameter::p4
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('p5]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: p5
+    staticElement: p5@132
+    staticType: null
+''');
   }
 
   test_newKeyword() async {
@@ -667,18 +1198,31 @@
       error(HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE, 49, 3),
     ]);
 
-    assertElement(
-      findNode.simple('A]'),
-      findElement.unnamedConstructor('A'),
-    );
-    assertElement(
-      findNode.simple('A.named]'),
-      findElement.class_('A'),
-    );
-    assertElement(
-      findNode.simple('named]'),
-      findElement.constructor('named', of: 'A'),
-    );
+    assertResolvedNodeText(findNode.commentReference('A]'), r'''
+CommentReference
+  newKeyword: new
+  expression: SimpleIdentifier
+    token: A
+    staticElement: self::@class::A::@constructor::•
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('A.named]'), r'''
+CommentReference
+  newKeyword: new
+  expression: PrefixedIdentifier
+    prefix: SimpleIdentifier
+      token: A
+      staticElement: self::@class::A
+      staticType: null
+    period: .
+    identifier: SimpleIdentifier
+      token: named
+      staticElement: self::@class::A::@constructor::named
+      staticType: null
+    staticElement: self::@class::A::@constructor::named
+    staticType: null
+''');
   }
 
   test_parameter_functionTyped() async {
@@ -687,10 +1231,13 @@
 foo(int bar()) {}
 ''');
 
-    assertElement(
-      findNode.simple('bar]'),
-      findElement.parameter('bar'),
-    );
+    assertResolvedNodeText(findNode.commentReference('bar]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: bar
+    staticElement: self::@function::foo::@parameter::bar
+    staticType: null
+''');
   }
 
   test_setter() async {
@@ -707,9 +1254,21 @@
 }
 ''');
 
-    var x = findElement.setter('x', of: 'A');
-    assertElement(findNode.simple('x] in A'), x);
-    assertElement(findNode.simple('x] in B'), x);
+    assertResolvedNodeText(findNode.commentReference('x] in A'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: x
+    staticElement: self::@class::A::@setter::x
+    staticType: null
+''');
+
+    assertResolvedNodeText(findNode.commentReference('x] in B'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: x
+    staticElement: self::@class::A::@setter::x
+    staticType: null
+''');
   }
 
   test_unqualifiedReferenceToNonLocalStaticMember() async {
@@ -722,9 +1281,12 @@
 class B extends A {}
 ''');
 
-    assertElement(
-      findNode.simple('foo]'),
-      findElement.method('foo', of: 'A'),
-    );
+    assertResolvedNodeText(findNode.commentReference('foo]'), r'''
+CommentReference
+  expression: SimpleIdentifier
+    token: foo
+    staticElement: self::@class::A::@method::foo
+    staticType: null
+''');
   }
 }
diff --git a/pkg/compiler/README.md b/pkg/compiler/README.md
index 939a841..a21d663 100644
--- a/pkg/compiler/README.md
+++ b/pkg/compiler/README.md
@@ -4,188 +4,101 @@
 
 ## Architecture
 
-The compiler is currently undergoing a long refactoring process. As you navigate
-this code you may find it helpful to understand how the compiler used to be,
-where it is going, and where it is today.
+The compiler is structured to operate in several phases. By default these phases
+are executed in sequence in a single process, but on some build systems, some of
+these phases are split into separate processes. As such, there is plenty of
+indirection and data representations used mostly for the purpose of serializing
+intermediate results during compilation.
 
-### The near future architecture
+The current compiler phases are:
 
-The compiler will operate in these general phases:
+  1. **common front-end**: Execute traditional front-end compilation phases.
+     Dart2js delegates to the common front-end (also used by DDC and the VM) to
+     do all front-end features, this includes:
+     *  parsing Dart source code,
+     *  type checking,
+     *  inferring implicit user types, like locals with a `var` declaration,
+     *  lowering or simplifying Dart features. For example, this is how many
+        syntactic features, like extension methods and list comprehensions, are
+        implemented.
+     * additional web-specific lowering or simplifications. For example,
+       expansion of JS-interop features and web specific implementation of
+       language features like late variables.
 
-  1. **load kernel**: Load all the code as kernel
-      * Collect dart sources transtively
-      * Convert to kernel AST
+    The result of this phase is a kernel AST which is serialized as a `.dill`
+    file.
 
-  (this will be handled by invoking the front-end package)
+  2. **modular analysis**: Using kernel as input, compute data recording
+     properties about each method in the program, especially around dependencies
+     and features they may need. We call this "impact data" (i1).
 
-  Alternatively, the compiler can start compilation directly from kernel files.
+     When the compiler runs as a single process, this is done lazily/on-demand
+     during the tree-shaking phase (below). However, this data can also be
+     computed independently for individual methods, files, or packages in the
+     application.  That makes it possible to run this modularly and in parallel.
 
-  2. **model**: Create a Dart model of the program
-     * The kernel ASTs could be used as a model, so this might be a no-op or just
-       creating a thin wrapper on top of kernel.
+     The result of this phase can be emitted as files containing impact data in
+     a serialized format.
 
-  3. **tree-shake and create world**: Build world of reachable code
-     * For each reachable piece of code:
-         * Compute impact (i1) from kernel AST
-     * Build a closed world (w1)
+  3. **tree-shake and create world**: Create a model to understand what parts of
+     the code are used by an application. This consists of:
+        * creating an intermediate representation called the "K model" that
+          wraps our kernel representation
+        * calculating which classes and methods are considered live in the
+          program. This is done by incrementally combining impact data (i1)
+          starting from `main`, then visiting reachable methods in the program
+          with an Rapid Type Analysis (RTA) algorithm to aggregate impacts
+          together.
 
-  4. **analyze**: Run a global analysis
-     * Assume closed world semantics (from w1)
-     * Produce a global result (g)
-        * Like today (g) will contain type and nullability information
-        * After we adopt strong-mode types, we want to explore simplifying this
-        to only contain native + nullability information.
+     The result of this phase is what we call a "closed world" (w1). The closed
+     world is also a datastructure that can answer interesting queries, such as:
+     Is this interface implemented by a single class? Is this method available
+     in any stubtype of some interface? The answers to these questions can help
+     the compiler generate higher quality JavaScript.
 
-  5. **codegen model**: Create a JS model of the program
-     * Model JavaScript specific concepts (like the split of constructor bodies
-       as separate elements) and provide a mapping to the Dart model
+  4. **global analysis**: Run a global analysis that assumes closed world
+     semantics (from w1) and propagates information across method boundaries to
+     further understand what values flow through the program. This phase is
+     very valuable in narrowing down possibilities that are ambiguous based
+     solely on type information written by developers. It often finds
+     oportunities that enable the compiler to devirtualize or inline method
+     calls, generate code specializations, or trigger performance optimizations.
 
-  6. **codegen and tree-shake**: Generate code, as needed
-     * For each reachable piece of code:
-        * build ssa graph from kernel ASTs and global results (g)
-        * optimize ssa
+     The result of this phase is a "global result" (g).
+
+  5. **codegen model**: Create a JS or backend model of the program. This is an
+     intermediate representation of the entities in the program we referred to
+     as the "J model". It is very similar to the "K model", but it is tailored
+     to model JavaScript specific concepts (like the split of constructor bodies
+     as separate elements) and provide a mapping to the Dart model.
+
+  6. **codegen**: Generate code for each method that is deemed necessary. This
+     includes:
+        * build an SSA graph from kernel ASTs and global results (g)
+        * optimize the SSA representation
         * compute impact (i2) from optimized code
         * emit JS ASTs for the code
-     * Build a codegen closed world (w2) from new impacts (i2)
 
-  7. **emit**: Assemble and minify the program
-     * Build program structure from the compiled pieces (w2)
+
+  7. **link tree-shake**: Using the results of codegen, we perform a second
+     round of tree-shaking. This is important because code that was deemed
+     reachable in (w1) may be found unreachable after optimizations. The process
+     is very similar to the earlier phase: we combine incrementally the codegen
+     impact data (i2) and compute a codegen closed world (w2).
+
+
+     When dart2js runs as a single process the codegen phase is done lazily and
+     on-demand, together with the tree-shaking phase.
+
+  8. **emit JavaScript files**: The final step is to assemble and minify the
+     final program. This includes:
+     * Build a JavaScript program structure from the compiled pieces (w2)
      * Use frequency namer to minify names.
      * Emit js and source map files.
 
-### The old architecture
+## Code organization
 
-The compiler used to operate as follows:
-
-  1. **load dart**: Load all source files
-     * Collect dart sources transtively
-     * Scan enough tokens to build import dependencies.
-
-  2. **model**: Create a Dart model (aka. Element Model) of the program
-     * Do a diet-parse of the program to create the high-level element model
-
-  3. **resolve and tree-shake**: Resolve and build world of reachable code (the
-     resolution enqueuer)
-     * For each reachable piece of code:
-        * Parse the full body of the function
-        * Resolve it and enqueue other pieces that are reachable
-        * Type check the body of the function
-
-  4. **analyze**: Run a global analysis
-     * Assume closed world semantics (from everything enqueued by the resolver)
-     * Produce a global result about type and nullability information of method
-       arguments, return values, and receivers of dynamic sends.
-
-  5. **codegen and tree-shake**: Generate code, as needed (via the codegen
-     enqueuer)
-     * For each reachable piece of code:
-        * build ssa graph from resolved source ASTs global results (g)
-        * optimize ssa
-        * enqueue visible dependencies
-        * emit js asts for the code
-
-  6. **emit**: Assemble and minify the program
-     * Build program structure from the compiled pieces
-     * Use frequency namer to minify names.
-     * Emit js and source map files.
-
-### The architecture today (which might be changing while you read this!)
-
-When using the `--use-kernel` flag, you can test the latest state of the
-compiler as we are migrating to the new architecture. Currently it works as
-follows:
-
-  1. **load dart**: (same as old compiler)
-
-  2. **model**: (same element model as old compiler)
-
-  3. **resolve, tree-shake and build world**: Build world of reachable code
-     * For each reachable piece of code:
-        * Parse full body of the function
-        * Resolve it from the parsed source ASTs
-        * Type check it (same as old compiler)
-        * Compute impact (i1) from resolved source ASTs (no kernel)
-     * Build a closed world (w1)
-
-  4. **kernelize**: Create kernel ASTs
-     * For all resolved elements in w1, compute their kernel representation using
-       the `rasta` visitor.
-
-  5. **analyze**: (almost same as old compiler)
-
-  6. **codegen and tree-shake**: Generate code, as needed
-     * For each reachable piece of code:
-        * build ssa graph from kernel ASTs (uses global results g)
-        * optimize ssa
-        * compute impact (i2) from optimized code
-        * emit js asts for the code
-     * Build a codegen closed world (w2) from new impacts (i2)
-
-  7. **emit**: (same as old compiler)
-
-Some additional details worth highlighting:
-
-  * tree-shaking is close to working as we want: the notion of a world and world
-    impacts are computed explicitly:
-
-     * In the old compiler, the resolver and code generator directly
-       enqueued items to be processed, there was no knowledge of what had
-       to be done other than in the algorithm itself.
-
-     * Now the information is computed explicitly in two ways:
-
-       * The dependencies of a single element are computed as an "impact"
-         object, these are derived from the structure of the
-         code (either the resolved code or the generated code).
-
-       * The closed world is now an explicit concept that can be replaced in the
-         compiler.
-
-     * This allows us to delete the resolver in the future and replace it
-       with a kernel loader, an impact builder from kernel, and a kernel world.
-
-     * There is an implementation of a kernel impact builder, but it is not yet
-       in use in the compiler pipeline (gated on replacing the Dart model)
-
-  * We still depend on the Dart model computed by resolution, but progress has
-    been made introducing an abstraction common to the new and old models. The
-    old model is the "Element model", the generic abstraction is called the
-    "Entity model". Some portions of the compiler now refer to the entity model.
-
-  * The ssa graph is built from the kernel ASTs, but it still depends on the old
-    element model computed from resolution (accessed via a kernel2Ast adapter).
-    The graph builder implementation covers a large chunk of the language
-    features, but is not complete (89% of langage & corelib tests are passing).
-
-  * Global analysis is still working on top of the dart2js ASTs.
-
-## Code organization and history
-
-The compiler package was initially intended to be compiler for multiple targets:
-Javascript, Dart (dart2dart), and dartino bytecodes. It has now evolved to be a
-Javascript only compiler, but some of the abstractions to support multiple
-targets still remain.
-
-### Possibly confusing terminology
-
-Some of the terminology in the compiler is confusing without knowing its
-history. We are cleaning this up as we are rearchitecting the system, but here
-are some of the legacy terminology we have:
-
-  * **target**: the output the compiler is producing. Nowdays it just
-    JavaScript, but in the past there was also Dart and dartino bytecodes.
-
-  * **backend**: pieces of the compiler that were target-specific.
-    Note: in the past we've used the term *backend* also for code that is used
-    in the frontend of the compiler that happens to be target-specific, as well
-    as and code that is used in the emitter or what traditionally is known
-    as the backend of the compiler.
-
-  * **frontend**: the parser, resolver, and other early stages of the compiler.
-    The front-end however makes target-specific choices. For example, to compile
-    a program with async-await, the dart2js backend needs to include some helper
-    functions that are used by the expanded async-await code, these helpers need
-    to be parsed by the frontend and added to the compilation pipeline.
+### Some terminology used in the compiler
 
   * **world**: the compiler exploits closed-world assumptions to do
     optimizations. The *world* encapsulates some of our knowledge of the
@@ -201,29 +114,22 @@
 
   * **model**: there are many models in the compiler:
 
-    * **element model**: this is an abstraction describing the elements seen in
-      Dart programs, like "libraries", "classes", "methods", etc.
-
-    * **entity model**: also describes elements seen in Dart programs, but it is
-      meant to be minimalistic and a super-hierarchy above the *element models*.
-      This is a newer addition, is an added abstraction to make it possible to
-      refactor our code from our old frontend to the kernel frontend.
-
-    * **Dart vs JS models**: the compiler in the past had a single model to
-      describe elements in the source and elements that were being compiled. In
-      the future we plan to have two. Both input model and output models will be
-      implementations of the *entity model*. The JS model is intended to have
-      concepts specific about generating code in JS (like constructor-bodies as
-      a separate entity than the constructor, closure classes, etc).
+    * **entity model**: this is an abstraction describing the elements seen in
+      Dart programs, like "libraries", "classes", "methods", etc. We currently
+      have two entity models, the "K model" (which is frontend centric and
+      usually maps 1:1 with kernel entities) and the "J model" (which is backend
+      centric).
 
     * **emitter model**: this is a model just used for dumping out the structure
       of the program in a .js text file. It doesn't have enough semantic meaning
-      to be a JS model for compilation at this moment.
+      to be a JS model for compilation, which is why there is a separate "J
+      model".
 
   * **enqueuer**: a work-queue used to achieve tree-shaking (or more precisely
     tree-growing): elements are added to the enqueuer as we recognize that they
-    are needed in a given application. Note that we even track how elements are
-    used, since some ways of using an element require more code than others.
+    are needed in a given application (as described by the impact data). Note
+    that we even track how elements are used, since some ways of using an
+    element require more code than others.
 
 ### Code layout
 
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 7ded407..1d64607 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -1125,7 +1125,7 @@
   }
 
   void _writeInfo(js.Node node) {
-    sink.writeCached<SourceInformation>(node.sourceInformation,
+    sink.writeCached<SourceInformation /*!*/ >(node.sourceInformation,
         (SourceInformation sourceInformation) {
       SourceInformation.writeToDataSink(sink, sourceInformation);
     });
@@ -2195,7 +2195,7 @@
         break;
     }
     SourceInformation sourceInformation =
-        source.readCached<SourceInformation>(() {
+        source.readCachedOrNull<SourceInformation>(() {
       return SourceInformation.readFromDataSource(source);
     });
     if (sourceInformation != null) {
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 3521af7..184e44e 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -22,6 +22,7 @@
 import 'compiler.dart' show Compiler;
 import 'constants/values.dart' show ConstantValue, InterceptorConstantValue;
 import 'deferred_load/output_unit.dart' show OutputUnit, deferredPartFileName;
+import 'dump_info_javascript_monitor.dart';
 import 'elements/entities.dart';
 import 'inferrer/abstract_value_domain.dart';
 import 'inferrer/types.dart'
@@ -1019,7 +1020,8 @@
   void reportInlined(FunctionEntity element, MemberEntity inlinedFrom);
 }
 
-class DumpInfoTask extends CompilerTask implements InfoReporter {
+class DumpInfoTask extends CompilerTask
+    implements DumpInfoJavaScriptMonitor, InfoReporter {
   final Compiler compiler;
   final bool useBinaryFormat;
 
@@ -1120,6 +1122,7 @@
   bool get shouldEmitText => !useBinaryFormat;
   // TODO(sigmund): delete the stack once we stop emitting the source text.
   final List<_CodeData> _stack = [];
+  @override // DumpInfoJavaScriptMonitor
   void enterNode(jsAst.Node node, int start) {
     var data = _nodeData[node];
     data?.start = start;
@@ -1129,6 +1132,7 @@
     }
   }
 
+  @override // DumpInfoJavaScriptMonitor
   void emit(String string) {
     if (shouldEmitText) {
       // Note: historically we emitted the full body of classes and methods, so
@@ -1138,6 +1142,7 @@
     }
   }
 
+  @override // DumpInfoJavaScriptMonitor
   void exitNode(jsAst.Node node, int start, int end, int closing) {
     var data = _nodeData[node];
     data?.end = end;
diff --git a/pkg/compiler/lib/src/dump_info_javascript_monitor.dart b/pkg/compiler/lib/src/dump_info_javascript_monitor.dart
new file mode 100644
index 0000000..da46ae1
--- /dev/null
+++ b/pkg/compiler/lib/src/dump_info_javascript_monitor.dart
@@ -0,0 +1,18 @@
+// 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.
+
+library dump_info.javascript_monitor;
+
+import 'package:js_ast/js_ast.dart' as jsAst show Node;
+
+/// Interface implemented by `DumpInfoTask` used to monitor the generated
+/// JavaScript as it is written.
+// TODO(48820): Remove this interface when `DumpInfoTask` is migrated.
+// TODO(sra): Perhaps `DumpInfoTask` should have a member that implements
+// `JavaScriptPrintingContext` instead of this very similar interface.
+abstract class DumpInfoJavaScriptMonitor {
+  void enterNode(jsAst.Node node, int start);
+  void emit(String string);
+  void exitNode(jsAst.Node node, int start, int end, int? closing);
+}
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart
index 869cfa7..58812cd 100644
--- a/pkg/compiler/lib/src/io/code_output.dart
+++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library dart2js.code_output;
 
 import '../../compiler_api.dart' as api show OutputSink;
@@ -28,8 +26,9 @@
   ///
   /// The inlining call-site was made from [pushLocation] and calls
   /// [inlinedMethodName].
+  // TODO(48820): We might have a [pushPosition].
   void addPush(
-      int targetOffset, SourceLocation pushPosition, String inlinedMethodName);
+      int targetOffset, SourceLocation? pushPosition, String inlinedMethodName);
 
   /// Record a return of an inlining call at the [targetOffset].
   ///
@@ -63,7 +62,7 @@
   }
 
   @override
-  void addPush(int targetOffset, SourceLocation sourceLocation,
+  void addPush(int targetOffset, SourceLocation? sourceLocation,
       String inlinedMethodName) {
     assert(targetOffset <= codeOutput.length);
     List<FrameEntry> frames = frameMarkers[targetOffset] ??= [];
@@ -137,12 +136,11 @@
 }
 
 abstract class AbstractCodeOutput extends CodeOutput {
-  final List<CodeOutputListener> _listeners;
+  final List<CodeOutputListener>? _listeners;
 
   AbstractCodeOutput([this._listeners]);
 
-  Map<String, _SourceLocationsImpl> sourceLocationsMap =
-      <String, _SourceLocationsImpl>{};
+  Map<String, _SourceLocationsImpl> sourceLocationsMap = {};
   @override
   bool isClosed = false;
 
@@ -198,7 +196,7 @@
 class CodeBuffer extends AbstractCodeOutput implements BufferedCodeOutput {
   StringBuffer buffer = StringBuffer();
 
-  CodeBuffer([List<CodeOutputListener> listeners]) : super(listeners);
+  CodeBuffer([List<CodeOutputListener>? listeners]) : super(listeners);
 
   @override
   void _addInternal(String text) {
@@ -225,7 +223,7 @@
   int length = 0;
   final api.OutputSink output;
 
-  StreamCodeOutput(this.output, [List<CodeOutputListener> listeners])
+  StreamCodeOutput(this.output, [List<CodeOutputListener>? listeners])
       : super(listeners);
 
   @override
diff --git a/pkg/compiler/lib/src/io/kernel_source_information.dart b/pkg/compiler/lib/src/io/kernel_source_information.dart
index 1634405..e8982b1 100644
--- a/pkg/compiler/lib/src/io/kernel_source_information.dart
+++ b/pkg/compiler/lib/src/io/kernel_source_information.dart
@@ -252,7 +252,7 @@
         if (node is ir.LocalFunction) {
           return _buildBody(node, node.function.body);
         } else if (node is ir.Member && node.function != null) {
-          return _buildBody(node, node.function.body);
+          return _buildBody(node, node.function /*!*/ .body);
         }
         break;
       default:
@@ -501,9 +501,4 @@
   KernelSourceLocation(ir.Location location, this.offset, this.sourceName)
       : sourceUri = location.file,
         super.fromLocation(location);
-
-  KernelSourceLocation.fromOther(KernelSourceLocation other, this.sourceName)
-      : sourceUri = other.sourceUri,
-        offset = other.offset,
-        super.fromOther(other);
 }
diff --git a/pkg/compiler/lib/src/io/position_information.dart b/pkg/compiler/lib/src/io/position_information.dart
index ee6cfd7..08b07ae 100644
--- a/pkg/compiler/lib/src/io/position_information.dart
+++ b/pkg/compiler/lib/src/io/position_information.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 /// Source information system mapping that attempts a semantic mapping between
 /// offsets of JavaScript code points to offsets of Dart code points.
 
@@ -27,22 +25,24 @@
   final SourceLocation startPosition;
 
   @override
-  final SourceLocation innerPosition;
+  final SourceLocation? innerPosition;
 
   @override
-  final List<FrameContext> inliningContext;
+  final List<FrameContext>? inliningContext;
 
   PositionSourceInformation(
-      this.startPosition, this.innerPosition, this.inliningContext);
+      this.startPosition, this.innerPosition, this.inliningContext) {
+    assert((startPosition as dynamic) != null);
+  }
 
   factory PositionSourceInformation.readFromDataSource(
       DataSourceReader source) {
     source.begin(tag);
     SourceLocation startPosition = source.readCached<SourceLocation>(
         () => SourceLocation.readFromDataSource(source));
-    SourceLocation innerPosition = source.readCached<SourceLocation>(
+    SourceLocation? innerPosition = source.readCachedOrNull<SourceLocation>(
         () => SourceLocation.readFromDataSource(source));
-    List<FrameContext> inliningContext =
+    List<FrameContext>? inliningContext =
         source.readListOrNull(() => FrameContext.readFromDataSource(source));
     source.end(tag);
     return PositionSourceInformation(
@@ -68,19 +68,17 @@
   @override
   List<SourceLocation> get sourceLocations {
     List<SourceLocation> list = [];
-    if (startPosition != null) {
-      list.add(startPosition);
-    }
+    list.add(startPosition);
     if (innerPosition != null) {
-      list.add(innerPosition);
+      list.add(innerPosition!);
     }
     return list;
   }
 
   @override
   SourceSpan get sourceSpan {
-    SourceLocation location = startPosition ?? innerPosition;
-    Uri uri = location.sourceUri;
+    SourceLocation location = startPosition;
+    Uri uri = location.sourceUri!;
     int offset = location.offset;
     return SourceSpan(uri, offset, offset);
   }
@@ -106,33 +104,23 @@
     StringBuffer sb = StringBuffer();
     sb.write('$uriText:');
     // Use 1-based line/column info to match usual dart tool output.
-    if (startPosition != null) {
-      sb.write('[${startPosition.line},'
-          '${startPosition.column}]');
-    }
+    sb.write('[${startPosition.line},'
+        '${startPosition.column}]');
     if (innerPosition != null) {
-      sb.write('-[${innerPosition.line},'
-          '${innerPosition.column}]');
+      sb.write('-[${innerPosition!.line},'
+          '${innerPosition!.column}]');
     }
     return sb.toString();
   }
 
   @override
   String get shortText {
-    if (startPosition != null) {
-      return _computeText(startPosition.sourceUri.pathSegments.last);
-    } else {
-      return _computeText(innerPosition.sourceUri.pathSegments.last);
-    }
+    return _computeText(startPosition.sourceUri!.pathSegments.last);
   }
 
   @override
   String toString() {
-    if (startPosition != null) {
-      return _computeText('${startPosition.sourceUri}');
-    } else {
-      return _computeText('${innerPosition.sourceUri}');
-    }
+    return _computeText('${startPosition.sourceUri}');
   }
 }
 
@@ -170,19 +158,19 @@
   List<SourceLocation> get sourceLocations => const [];
 
   @override
-  SourceSpan get sourceSpan => SourceSpan(null, null, null);
+  SourceSpan get sourceSpan => SourceSpan.unknown();
 }
 
 /// The start, end and closing offsets for a [js.Node].
 class CodePosition {
   final int startPosition;
   final int endPosition;
-  final int closingPosition;
+  final int? closingPosition;
 
   CodePosition(this.startPosition, this.endPosition, this.closingPosition);
 
   // ignore: MISSING_RETURN
-  int getPosition(CodePositionKind kind) {
+  int? getPosition(CodePositionKind kind) {
     switch (kind) {
       case CodePositionKind.START:
         return startPosition;
@@ -202,7 +190,7 @@
 
 /// A map from a [js.Node] to its [CodePosition].
 abstract class CodePositionMap {
-  CodePosition operator [](js.Node node);
+  CodePosition? operator [](js.Node node);
 }
 
 /// Registry for mapping [js.Node]s to their [CodePosition].
@@ -211,7 +199,7 @@
       Map<js.Node, CodePosition>.identity();
 
   void registerPositions(
-      js.Node node, int startPosition, int endPosition, int closingPosition) {
+      js.Node node, int startPosition, int endPosition, int? closingPosition) {
     registerCodePosition(
         node, CodePosition(startPosition, endPosition, closingPosition));
   }
@@ -221,7 +209,7 @@
   }
 
   @override
-  CodePosition operator [](js.Node node) => _codePositionMap[node];
+  CodePosition? operator [](js.Node node) => _codePositionMap[node];
 }
 
 /// Enum values for the part of a Dart node used for the source location offset.
@@ -259,9 +247,9 @@
 }
 
 // ignore: MISSING_RETURN
-SourceLocation getSourceLocation(SourceInformation sourceInformation,
+SourceLocation? getSourceLocation(SourceInformation sourceInformation,
     [SourcePositionKind sourcePositionKind = SourcePositionKind.START]) {
-  if (sourceInformation == null) return null;
+  assert((sourceInformation as dynamic) != null);
   switch (sourcePositionKind) {
     case SourcePositionKind.START:
       return sourceInformation.startPosition;
@@ -319,12 +307,12 @@
 
   final CodePositionRecorder codePositionRecorder = CodePositionRecorder();
   final SourceInformationReader reader;
-  CodePositionMap codePositionMap;
-  List<TraceListener> traceListeners;
-  InliningTraceListener inliningListener;
+  late final CodePositionMap codePositionMap;
+  late final List<TraceListener> traceListeners;
+  late final InliningTraceListener inliningListener;
 
   PositionSourceInformationProcessor(SourceMapperProvider provider, this.reader,
-      [Coverage coverage]) {
+      [Coverage? coverage]) {
     codePositionMap = coverage != null
         ? CodePositionCoverage(codePositionRecorder, coverage)
         : codePositionRecorder;
@@ -341,12 +329,12 @@
   @override
   void process(js.Node node, BufferedCodeOutput code) {
     JavaScriptTracer(codePositionMap, reader, traceListeners).apply(node);
-    inliningListener?.finish();
+    inliningListener.finish();
   }
 
   @override
   void onPositions(
-      js.Node node, int startPosition, int endPosition, int closingPosition) {
+      js.Node node, int startPosition, int endPosition, int? closingPosition) {
     codePositionRecorder.registerPositions(
         node, startPosition, endPosition, closingPosition);
   }
@@ -354,23 +342,23 @@
 
 /// Visitor that computes [SourceInformation] for a [js.Node] using information
 /// attached to the node itself or alternatively from child nodes.
-class NodeSourceInformation extends js.BaseVisitor<SourceInformation> {
+class NodeSourceInformation extends js.BaseVisitor<SourceInformation?> {
   final SourceInformationReader reader;
 
   const NodeSourceInformation(this.reader);
 
-  SourceInformation visit(js.Node node) => node?.accept(this);
+  SourceInformation? visit(js.Node? node) => node?.accept(this);
 
   @override
-  SourceInformation visitNode(js.Node node) =>
+  SourceInformation? visitNode(js.Node node) =>
       reader.getSourceInformation(node);
 
   @override
-  SourceInformation visitComment(js.Comment node) => null;
+  SourceInformation? visitComment(js.Comment node) => null;
 
   @override
-  SourceInformation visitExpressionStatement(js.ExpressionStatement node) {
-    SourceInformation sourceInformation = reader.getSourceInformation(node);
+  SourceInformation? visitExpressionStatement(js.ExpressionStatement node) {
+    SourceInformation? sourceInformation = reader.getSourceInformation(node);
     if (sourceInformation != null) {
       return sourceInformation;
     }
@@ -378,14 +366,14 @@
   }
 
   @override
-  SourceInformation visitVariableDeclarationList(
+  SourceInformation? visitVariableDeclarationList(
       js.VariableDeclarationList node) {
-    SourceInformation sourceInformation = reader.getSourceInformation(node);
+    SourceInformation? sourceInformation = reader.getSourceInformation(node);
     if (sourceInformation != null) {
       return sourceInformation;
     }
     for (js.Node declaration in node.declarations) {
-      SourceInformation sourceInformation = visit(declaration);
+      SourceInformation? sourceInformation = visit(declaration);
       if (sourceInformation != null) {
         return sourceInformation;
       }
@@ -394,9 +382,9 @@
   }
 
   @override
-  SourceInformation visitVariableInitialization(
+  SourceInformation? visitVariableInitialization(
       js.VariableInitialization node) {
-    SourceInformation sourceInformation = reader.getSourceInformation(node);
+    SourceInformation? sourceInformation = reader.getSourceInformation(node);
     if (sourceInformation != null) {
       return sourceInformation;
     }
@@ -404,8 +392,8 @@
   }
 
   @override
-  SourceInformation visitAssignment(js.Assignment node) {
-    SourceInformation sourceInformation = reader.getSourceInformation(node);
+  SourceInformation? visitAssignment(js.Assignment node) {
+    SourceInformation? sourceInformation = reader.getSourceInformation(node);
     if (sourceInformation != null) {
       return sourceInformation;
     }
@@ -417,7 +405,7 @@
 abstract class NodeToSourceInformationMixin {
   SourceInformationReader get reader;
 
-  SourceInformation computeSourceInformation(js.Node node) {
+  SourceInformation? computeSourceInformation(js.Node node) {
     return NodeSourceInformation(reader).visit(node);
   }
 }
@@ -428,37 +416,38 @@
   final SourceMapper sourceMapper;
   @override
   final SourceInformationReader reader;
-  final Map<int, List<FrameContext>> _frames = {};
+  final Map<int, List<FrameContext>?> _frames = {};
 
   InliningTraceListener(this.sourceMapper, this.reader);
 
   @override
   void onStep(js.Node node, Offset offset, StepKind kind) {
-    SourceInformation sourceInformation = computeSourceInformation(node);
+    SourceInformation? sourceInformation = computeSourceInformation(node);
     if (sourceInformation == null) return;
     // TODO(sigmund): enable this assertion.
     // assert(offset.value != null, "Expected a valid offset: $node $offset");
-    if (offset.value == null) return;
+    final offsetValue = offset.value;
+    if (offsetValue == null) return;
 
     // TODO(sigmund): enable this assertion
     //assert(_frames[offset.value] == null,
     //     "Expect a single entry per offset: $offset $node");
-    if (_frames[offset.value] != null) return;
+    if (_frames[offsetValue] != null) return;
 
     // During tracing we only collect information per offset because the tracer
     // visits nodes in tree order. We'll later sort the data by offset before
     // registering the frame data with [SourceMapper].
     if (kind == StepKind.FUN_EXIT) {
-      _frames[offset.value] = null;
+      _frames[offsetValue] = null;
     } else {
-      _frames[offset.value] = sourceInformation.inliningContext;
+      _frames[offsetValue] = sourceInformation.inliningContext;
     }
   }
 
   /// Converts the inlining context data collected during tracing into push/pop
   /// stack operations that will be emitted with the source-map files.
   void finish() {
-    List<FrameContext> lastInliningContext;
+    List<FrameContext>? lastInliningContext;
     for (var offset in _frames.keys.toList()..sort()) {
       var newInliningContext = _frames[offset];
 
@@ -471,7 +460,7 @@
       int popCount = 0;
       List<FrameContext> pushes = const [];
       if (newInliningContext == null) {
-        popCount = lastInliningContext.length;
+        popCount = lastInliningContext!.length;
         isEmpty = true;
       } else if (lastInliningContext == null) {
         pushes = newInliningContext;
@@ -563,7 +552,7 @@
   ///                                    ^
   @override
   void onStep(js.Node node, Offset offset, StepKind kind) {
-    int codeLocation = offset.value;
+    int? codeLocation = offset.value;
     if (codeLocation == null) return;
 
     if (kind == StepKind.NO_INFO) {
@@ -571,11 +560,11 @@
       return;
     }
 
-    SourceInformation sourceInformation = computeSourceInformation(node);
+    SourceInformation? sourceInformation = computeSourceInformation(node);
     if (sourceInformation == null) return;
 
     void registerPosition(SourcePositionKind sourcePositionKind) {
-      SourceLocation sourceLocation =
+      SourceLocation? sourceLocation =
           getSourceLocation(sourceInformation, sourcePositionKind);
       if (sourceLocation != null) {
         sourceMapper.register(node, codeLocation, sourceLocation);
@@ -595,7 +584,7 @@
         break;
       case StepKind.CALL:
         CallPosition callPosition =
-            CallPosition.getSemanticPositionForCall(node);
+            CallPosition.getSemanticPositionForCall(node as js.Call);
         registerPosition(callPosition.sourcePositionKind);
         break;
       case StepKind.ACCESS:
@@ -686,7 +675,7 @@
   /// (@ marks the current JavaScript position and ^ point to the mapped Dart
   /// code position.)
   static CallPosition getSemanticPositionForCall(js.Call node) {
-    js.Expression access = js.undefer(node.target);
+    js.Expression access = js.undefer(node.target) as js.Expression;
     if (access is js.PropertyAccess) {
       js.Node target = access;
       bool pureAccess = false;
@@ -758,6 +747,10 @@
 ///
 /// Currently [subexpressionOffset] is used since it corresponds the most to the
 /// offset used by most browsers.
+///
+// TODO(sra): Any or all of the values can be `null`. Investigate why this
+// happens. Since we are writing a JavaScript AST to an output, we should be
+// able to have non-null values.
 class Offset {
   /// The offset of the enclosing statement relative to the beginning of the
   /// file.
@@ -769,7 +762,7 @@
   ///     ^                  // the statement offset of the `*.bar()` call
   ///     ^                  // the statement offset of the `baz()` call
   ///
-  final int statementOffset;
+  final int? statementOffset;
 
   /// The `subexpression` offset of the step. This is the (mostly) unique
   /// offset relative to the beginning of the file, that identifies the
@@ -786,7 +779,7 @@
   /// the `foo()` its execution is identified by the `bar` identifier more than
   /// the foo identifier.
   ///
-  final int subexpressionOffset;
+  final int? subexpressionOffset;
 
   /// The `left-to-right` offset of the step. This is like [subexpressionOffset]
   /// but restricted so that the offset of each subexpression in execution
@@ -802,12 +795,12 @@
   /// Here, `baz()` is executed before `foo()` so we need to use 'f' as its best
   /// position under the restriction.
   ///
-  final int leftToRightOffset;
+  final int? leftToRightOffset;
 
   Offset(
       this.statementOffset, this.leftToRightOffset, this.subexpressionOffset);
 
-  int get value => subexpressionOffset;
+  int? get value => subexpressionOffset;
 
   @override
   String toString() {
@@ -878,13 +871,13 @@
   List steps = [];
 
   /// The offset of the current statement.
-  int statementOffset;
+  int? statementOffset;
 
   /// The current offset in left-to-right progression.
-  int leftToRightOffset;
+  int? leftToRightOffset;
 
   /// The offset of the surrounding statement, used for the first subexpression.
-  int offsetPosition;
+  int? offsetPosition;
 
   bool active;
 
@@ -921,7 +914,7 @@
   void apply(js.Node node) {
     notifyStart(node);
 
-    int startPosition = getSyntaxOffset(node, kind: CodePositionKind.START);
+    int? startPosition = getSyntaxOffset(node, kind: CodePositionKind.START);
     Offset startOffset = getOffsetForNode(node, startPosition);
     notifyStep(node, startOffset, StepKind.NO_INFO, force: true);
 
@@ -930,11 +923,11 @@
   }
 
   @override
-  visitNode(js.Node node) {
+  void visitNode(js.Node node) {
     node.visitChildren(this);
   }
 
-  visit(js.Node node, [BranchKind branch, value]) {
+  visit(js.Node? node, [BranchKind? branch, value]) {
     if (node != null) {
       if (branch != null) {
         notifyPushBranch(branch, value);
@@ -946,11 +939,9 @@
     }
   }
 
-  visitList(List<js.Node> nodeList) {
-    if (nodeList != null) {
-      for (js.Node node in nodeList) {
-        visit(node);
-      }
+  void visitList(List<js.Node> nodeList) {
+    for (js.Node node in nodeList) {
+      visit(node);
     }
   }
 
@@ -971,7 +962,7 @@
     Offset exitOffset = getOffsetForNode(node, statementOffset);
     notifyStep(node, exitOffset, StepKind.FUN_EXIT);
     if (active && !activeBefore) {
-      int endPosition = getSyntaxOffset(node, kind: CodePositionKind.END);
+      int? endPosition = getSyntaxOffset(node, kind: CodePositionKind.END);
       Offset endOffset = getOffsetForNode(node, endPosition);
       notifyStep(node, endOffset, StepKind.NO_INFO);
     }
@@ -995,9 +986,9 @@
     }
   }
 
-  int getSyntaxOffset(js.Node node,
+  int? getSyntaxOffset(js.Node node,
       {CodePositionKind kind = CodePositionKind.START}) {
-    CodePosition codePosition = codePositions[node];
+    CodePosition? codePosition = codePositions[node];
     if (codePosition != null) {
       return codePosition.getPosition(kind);
     }
@@ -1005,7 +996,7 @@
   }
 
   visitSubexpression(
-      js.Node parent, js.Expression child, int codeOffset, StepKind kind) {
+      js.Node parent, js.Expression child, int? codeOffset, StepKind kind) {
     var oldSteps = steps;
     steps = [];
     offsetPosition = codeOffset;
@@ -1033,13 +1024,13 @@
   @override
   visitCall(js.Call node) {
     visit(node.target);
-    int oldPosition = offsetPosition;
+    int? oldPosition = offsetPosition;
     offsetPosition = null;
     visitList(node.arguments);
     offsetPosition = oldPosition;
     CallPosition callPosition = CallPosition.getSemanticPositionForCall(node);
     js.Node positionNode = callPosition.node;
-    int callOffset =
+    int? callOffset =
         getSyntaxOffset(positionNode, kind: callPosition.codePositionKind);
     if (offsetPosition == null) {
       // Use the call offset if this is not the first subexpression.
@@ -1054,7 +1045,7 @@
   @override
   visitNew(js.New node) {
     visit(node.target);
-    int oldPosition = offsetPosition;
+    int? oldPosition = offsetPosition;
     offsetPosition = null;
     visitList(node.arguments);
     offsetPosition = oldPosition;
@@ -1135,26 +1126,29 @@
 
   @override
   visitFor(js.For node) {
-    int offset = statementOffset = getSyntaxOffset(node);
+    int? offset = statementOffset = getSyntaxOffset(node);
     statementOffset = offset;
     leftToRightOffset = null;
-    if (node.init != null) {
+    final init = node.init;
+    if (init != null) {
       visitSubexpression(
-          node, node.init, getSyntaxOffset(node), StepKind.FOR_INITIALIZER);
+          node, init, getSyntaxOffset(node), StepKind.FOR_INITIALIZER);
     }
 
-    if (node.condition != null) {
-      visitSubexpression(node, node.condition, getSyntaxOffset(node.condition),
-          StepKind.FOR_CONDITION);
+    final condition = node.condition;
+    if (condition != null) {
+      visitSubexpression(
+          node, condition, getSyntaxOffset(condition), StepKind.FOR_CONDITION);
     }
 
     notifyPushBranch(BranchKind.LOOP);
     visit(node.body);
 
     statementOffset = offset;
-    if (node.update != null) {
+    final update = node.update;
+    if (update != null) {
       visitSubexpression(
-          node, node.update, getSyntaxOffset(node.update), StepKind.FOR_UPDATE);
+          node, update, getSyntaxOffset(update), StepKind.FOR_UPDATE);
     }
 
     notifyPopBranch();
@@ -1163,10 +1157,8 @@
   @override
   visitWhile(js.While node) {
     statementOffset = getSyntaxOffset(node);
-    if (node.condition != null) {
-      visitSubexpression(node, node.condition, getSyntaxOffset(node),
-          StepKind.WHILE_CONDITION);
-    }
+    visitSubexpression(
+        node, node.condition, getSyntaxOffset(node), StepKind.WHILE_CONDITION);
     statementOffset = null;
     leftToRightOffset = null;
 
@@ -1177,10 +1169,9 @@
   visitDo(js.Do node) {
     statementOffset = getSyntaxOffset(node);
     visit(node.body);
-    if (node.condition != null) {
-      visitSubexpression(node, node.condition, getSyntaxOffset(node.condition),
-          StepKind.DO_CONDITION);
-    }
+    final condition = node.condition;
+    visitSubexpression(
+        node, condition, getSyntaxOffset(condition), StepKind.DO_CONDITION);
     statementOffset = null;
     leftToRightOffset = null;
   }
@@ -1323,16 +1314,16 @@
     visit(node.value);
   }
 
-  Offset getOffsetForNode(js.Node node, int codeOffset) {
+  Offset getOffsetForNode(js.Node node, int? codeOffset) {
     if (codeOffset == null) {
-      CodePosition codePosition = codePositions[node];
+      CodePosition? codePosition = codePositions[node];
       if (codePosition != null) {
         codeOffset = codePosition.startPosition;
       }
     }
     if (leftToRightOffset != null &&
         codeOffset != null &&
-        leftToRightOffset < codeOffset) {
+        leftToRightOffset! < codeOffset) {
       leftToRightOffset = codeOffset;
     }
     if (leftToRightOffset == null) {
@@ -1370,15 +1361,16 @@
     _nodesWithoutOffset.clear();
 
     _nodesWithoutInfoCount += _nodesWithoutInfo.length;
+
     for (js.Node node in _nodesWithoutInfo) {
+      Type type;
       if (node is js.ExpressionStatement) {
-        _nodesWithoutInfoCountByType.putIfAbsent(
-            node.expression.runtimeType, () => 0);
-        _nodesWithoutInfoCountByType[node.expression.runtimeType]++;
+        type = node.expression.runtimeType;
       } else {
-        _nodesWithoutInfoCountByType.putIfAbsent(node.runtimeType, () => 0);
-        _nodesWithoutInfoCountByType[node.runtimeType]++;
+        type = node.runtimeType;
       }
+      _nodesWithoutInfoCountByType.update(type, (count) => count + 1,
+          ifAbsent: () => 1);
     }
     _nodesWithoutInfo.clear();
   }
@@ -1412,12 +1404,12 @@
       sb.write(') by runtime type:');
       List<Type> types = _nodesWithoutInfoCountByType.keys.toList();
       types.sort((a, b) {
-        return -_nodesWithoutInfoCountByType[a]
-            .compareTo(_nodesWithoutInfoCountByType[b]);
+        return -_nodesWithoutInfoCountByType[a]!
+            .compareTo(_nodesWithoutInfoCountByType[b]!);
       });
 
       types.forEach((Type type) {
-        int count = _nodesWithoutInfoCountByType[type];
+        int count = _nodesWithoutInfoCountByType[type]!;
         sb.write('\n ');
         sb.write(count);
         sb.write(' ');
@@ -1446,7 +1438,7 @@
 
   @override
   void onStep(js.Node node, Offset offset, StepKind kind) {
-    SourceInformation sourceInformation = computeSourceInformation(node);
+    SourceInformation? sourceInformation = computeSourceInformation(node);
     if (sourceInformation != null) {
       coverage.registerNodeWithInfo(node);
     } else {
@@ -1468,8 +1460,8 @@
   CodePositionCoverage(this.codePositions, this.coverage);
 
   @override
-  CodePosition operator [](js.Node node) {
-    CodePosition codePosition = codePositions[node];
+  CodePosition? operator [](js.Node node) {
+    CodePosition? codePosition = codePositions[node];
     if (codePosition == null) {
       coverage.registerNodesWithoutOffset(node);
     }
diff --git a/pkg/compiler/lib/src/io/source_information.dart b/pkg/compiler/lib/src/io/source_information.dart
index 0171346..bbf7969 100644
--- a/pkg/compiler/lib/src/io/source_information.dart
+++ b/pkg/compiler/lib/src/io/source_information.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library dart2js.source_information;
 
 import 'package:kernel/ast.dart' as ir;
@@ -22,9 +20,7 @@
 
   static SourceInformation readFromDataSource(DataSourceReader source) {
     int hasSourceInformation = source.readInt();
-    if (hasSourceInformation == 0) {
-      return null;
-    } else if (hasSourceInformation == 1) {
+    if (hasSourceInformation == 1) {
       return const SourceMappedMarker();
     } else {
       assert(hasSourceInformation == 2);
@@ -34,13 +30,12 @@
 
   static void writeToDataSink(
       DataSinkWriter sink, SourceInformation sourceInformation) {
-    if (sourceInformation == null) {
-      sink.writeInt(0);
-    } else if (sourceInformation is SourceMappedMarker) {
+    if (sourceInformation is SourceMappedMarker) {
       sink.writeInt(1);
     } else {
       sink.writeInt(2);
-      PositionSourceInformation positionSourceInformation = sourceInformation;
+      PositionSourceInformation positionSourceInformation =
+          sourceInformation as PositionSourceInformation;
       positionSourceInformation.writeToDataSinkInternal(sink);
     }
   }
@@ -48,21 +43,21 @@
   SourceSpan get sourceSpan;
 
   /// The source location associated with the start of the JS node.
-  SourceLocation get startPosition => null;
+  SourceLocation? get startPosition => null;
 
   /// The source location associated with an inner of the JS node.
   ///
   /// The inner position is for instance `foo()` in `o.foo()`.
-  SourceLocation get innerPosition => null;
+  SourceLocation? get innerPosition => null;
 
   /// The source location associated with the end of the JS node.
-  SourceLocation get endPosition => null;
+  SourceLocation? get endPosition => null;
 
   /// A list containing start, inner, and end positions.
   List<SourceLocation> get sourceLocations;
 
   /// A list of inlining context locations.
-  List<FrameContext> get inliningContext => null;
+  List<FrameContext>? get inliningContext => null;
 
   /// Return a short textual representation of the source location.
   String get shortText;
@@ -118,7 +113,7 @@
   }
 
   /// Generate [SourceInformation] marker for non-preamble code.
-  SourceInformation buildSourceMappedMarker() => null;
+  SourceInformation? buildSourceMappedMarker() => null;
 
   /// Called when compilation has completed.
   void onComplete() {}
@@ -135,123 +130,124 @@
       this;
 
   /// Generate [SourceInformation] for the declaration of the [member].
-  SourceInformation buildDeclaration(covariant MemberEntity member) => null;
+  SourceInformation? buildDeclaration(covariant MemberEntity member) => null;
 
   /// Generate [SourceInformation] for the stub of [callStructure] for [member].
-  SourceInformation buildStub(
+  SourceInformation? buildStub(
           covariant FunctionEntity function, CallStructure callStructure) =>
       null;
 
   /// Generate [SourceInformation] for the generic [node].
   @deprecated
-  SourceInformation buildGeneric(ir.Node node) => null;
+  SourceInformation? buildGeneric(ir.Node node) => null;
 
   /// Generate [SourceInformation] for an instantiation of a class using [node]
   /// for the source position.
-  SourceInformation buildCreate(ir.Node node) => null;
+  SourceInformation? buildCreate(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the return [node].
-  SourceInformation buildReturn(ir.Node node) => null;
+  SourceInformation? buildReturn(ir.Node node) => null;
 
   /// Generate [SourceInformation] for an implicit return in [element].
-  SourceInformation buildImplicitReturn(covariant MemberEntity element) => null;
+  SourceInformation? buildImplicitReturn(covariant MemberEntity element) =>
+      null;
 
   /// Generate [SourceInformation] for the loop [node].
-  SourceInformation buildLoop(ir.Node node) => null;
+  SourceInformation? buildLoop(ir.Node node) => null;
 
   /// Generate [SourceInformation] for a read access like `a.b`.
-  SourceInformation buildGet(ir.Node node) => null;
+  SourceInformation? buildGet(ir.Node node) => null;
 
   /// Generate [SourceInformation] for a write access like `a.b = 3`.
-  SourceInformation buildSet(ir.Node node) => null;
+  SourceInformation? buildSet(ir.Node node) => null;
 
   /// Generate [SourceInformation] for a call in [node].
-  SourceInformation buildCall(ir.Node receiver, ir.Node call) => null;
+  SourceInformation? buildCall(ir.Node receiver, ir.Node call) => null;
 
   /// Generate [SourceInformation] for the if statement in [node].
-  SourceInformation buildIf(ir.Node node) => null;
+  SourceInformation? buildIf(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the constructor invocation in [node].
-  SourceInformation buildNew(ir.Node node) => null;
+  SourceInformation? buildNew(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the throw in [node].
-  SourceInformation buildThrow(ir.Node node) => null;
+  SourceInformation? buildThrow(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the assert in [node].
-  SourceInformation buildAssert(ir.Node node) => null;
+  SourceInformation? buildAssert(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the assignment in [node].
-  SourceInformation buildAssignment(ir.Node node) => null;
+  SourceInformation? buildAssignment(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the variable declaration inserted as
   /// first statement of a function.
-  SourceInformation buildVariableDeclaration() => null;
+  SourceInformation? buildVariableDeclaration() => null;
 
   /// Generate [SourceInformation] for the await [node].
-  SourceInformation buildAwait(ir.Node node) => null;
+  SourceInformation? buildAwait(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the yield or yield* [node].
-  SourceInformation buildYield(ir.Node node) => null;
+  SourceInformation? buildYield(ir.Node node) => null;
 
   /// Generate [SourceInformation] for async/await boiler plate code.
-  SourceInformation buildAsyncBody() => null;
+  SourceInformation? buildAsyncBody() => null;
 
   /// Generate [SourceInformation] for exiting async/await code.
-  SourceInformation buildAsyncExit() => null;
+  SourceInformation? buildAsyncExit() => null;
 
   /// Generate [SourceInformation] for an invocation of a foreign method.
-  SourceInformation buildForeignCode(ir.Node node) => null;
+  SourceInformation? buildForeignCode(ir.Node node) => null;
 
   /// Generate [SourceInformation] for a string interpolation of [node].
-  SourceInformation buildStringInterpolation(ir.Node node) => null;
+  SourceInformation? buildStringInterpolation(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the for-in `iterator` access in [node].
-  SourceInformation buildForInIterator(ir.Node node) => null;
+  SourceInformation? buildForInIterator(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the for-in `moveNext` call in [node].
-  SourceInformation buildForInMoveNext(ir.Node node) => null;
+  SourceInformation? buildForInMoveNext(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the for-in `current` access in [node].
-  SourceInformation buildForInCurrent(ir.Node node) => null;
+  SourceInformation? buildForInCurrent(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the for-in variable assignment in [node].
-  SourceInformation buildForInSet(ir.Node node) => null;
+  SourceInformation? buildForInSet(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the operator `[]` access in [node].
-  SourceInformation buildIndex(ir.Node node) => null;
+  SourceInformation? buildIndex(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the operator `[]=` assignment in [node].
-  SourceInformation buildIndexSet(ir.Node node) => null;
+  SourceInformation? buildIndexSet(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the binary operation in [node].
-  SourceInformation buildBinary(ir.Node node) => null;
+  SourceInformation? buildBinary(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the unary operation in [node].
-  SourceInformation buildUnary(ir.Node node) => null;
+  SourceInformation? buildUnary(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the try statement in [node].
-  SourceInformation buildTry(ir.Node node) => null;
+  SourceInformation? buildTry(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the unary operator in [node].
-  SourceInformation buildCatch(ir.Node node) => null;
+  SourceInformation? buildCatch(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the is-test in [node].
-  SourceInformation buildIs(ir.Node node) => null;
+  SourceInformation? buildIs(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the as-cast in [node].
-  SourceInformation buildAs(ir.Node node) => null;
+  SourceInformation? buildAs(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the switch statement [node].
-  SourceInformation buildSwitch(ir.Node node) => null;
+  SourceInformation? buildSwitch(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the switch case in [node].
-  SourceInformation buildSwitchCase(ir.Node node) => null;
+  SourceInformation? buildSwitchCase(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the list literal in [node].
-  SourceInformation buildListLiteral(ir.Node node) => null;
+  SourceInformation? buildListLiteral(ir.Node node) => null;
 
   /// Generate [SourceInformation] for the break/continue in [node].
-  SourceInformation buildGoto(ir.Node node) => null;
+  SourceInformation? buildGoto(ir.Node node) => null;
 }
 
 /// A location in a source file.
@@ -261,7 +257,13 @@
   const SourceLocation();
 
   /// The absolute URI of the source file of this source location.
-  Uri get sourceUri;
+
+  // TODO(48820): [sourceUri] is nullable due to `NoSourceLocationMarker`. We
+  // would not need nullability of we could replace all
+  // `NoSourceLocationMarker`s with `null`, or rearranged the `SourceLocation`
+  // class hierarchy. `NoSourceLocationMarker` and `null` have different effects
+  // on the generated source-map files.
+  Uri? get sourceUri;
 
   /// The character offset of the this source location into the source file.
   int get offset;
@@ -273,41 +275,38 @@
   int get column;
 
   /// The name associated with this source location, if any.
-  String get sourceName;
+  String? get sourceName;
 
   static SourceLocation readFromDataSource(DataSourceReader source) {
     int hasSourceLocation = source.readInt();
-    if (hasSourceLocation == 0) {
-      return null;
-    } else if (hasSourceLocation == 1) {
+    if (hasSourceLocation == 1) {
       return const NoSourceLocationMarker();
-    } else {
-      assert(hasSourceLocation == 2);
+    } else if (hasSourceLocation == 2) {
       source.begin(tag);
       Uri sourceUri = source.readUri();
       int offset = source.readInt();
       int line = source.readInt();
       int column = source.readInt();
-      String sourceName = source.readString();
+      String? sourceName = source.readStringOrNull();
       source.end(tag);
       return DirectSourceLocation(sourceUri, offset, line, column, sourceName);
+    } else {
+      throw StateError('Unknown hasSourceLocation value: $hasSourceLocation');
     }
   }
 
   static void writeToDataSink(
       DataSinkWriter sink, SourceLocation sourceLocation) {
-    if (sourceLocation == null) {
-      sink.writeInt(0);
-    } else if (sourceLocation is NoSourceLocationMarker) {
+    if (sourceLocation is NoSourceLocationMarker) {
       sink.writeInt(1);
     } else {
       sink.writeInt(2);
       sink.begin(tag);
-      sink.writeUri(sourceLocation.sourceUri);
+      sink.writeUri(sourceLocation.sourceUri!);
       sink.writeInt(sourceLocation.offset);
       sink.writeInt(sourceLocation.line);
       sink.writeInt(sourceLocation.column);
-      sink.writeString(sourceLocation.sourceName);
+      sink.writeStringOrNull(sourceLocation.sourceName);
       sink.end(tag);
     }
   }
@@ -328,7 +327,7 @@
         sourceName == other.sourceName;
   }
 
-  String get shortText => '${sourceUri?.pathSegments?.last}:[$line,$column]';
+  String get shortText => '${sourceUri?.pathSegments.last}:[$line,$column]';
 
   @override
   String toString() => '${sourceUri}:[${line},${column}]';
@@ -348,7 +347,7 @@
   final int column;
 
   @override
-  final String sourceName;
+  final String? sourceName;
 
   DirectSourceLocation(
       this.sourceUri, this.offset, this.line, this.column, this.sourceName);
@@ -356,37 +355,36 @@
 
 /// A location in a source file.
 abstract class AbstractSourceLocation extends SourceLocation {
-  final SourceFile _sourceFile;
-  ir.Location _location;
+  final SourceFile? _sourceFile;
+  ir.Location? _location;
 
-  AbstractSourceLocation(this._sourceFile) {
+  AbstractSourceLocation(SourceFile this._sourceFile) {
     assert(
-        offset < _sourceFile.length,
+        offset < _sourceFile!.length,
         failedAt(
             SourceSpan(sourceUri, 0, 0),
             "Invalid source location in ${sourceUri}: "
-            "offset=$offset, length=${_sourceFile.length}."));
+            "offset=$offset, length=${_sourceFile!.length}."));
   }
 
-  AbstractSourceLocation.fromLocation(this._location) : _sourceFile = null;
-
-  AbstractSourceLocation.fromOther(AbstractSourceLocation location)
-      : this.fromLocation(location._location);
+  AbstractSourceLocation.fromLocation(ir.Location location)
+      : _location = location,
+        _sourceFile = null;
 
   @override
-  Uri get sourceUri => _sourceFile.uri;
+  Uri get sourceUri => _sourceFile!.uri;
 
   @override
   int get offset;
 
   @override
-  int get line => (_location ??= _sourceFile.getLocation(offset)).line;
+  int get line => (_location ??= _sourceFile!.getLocation(offset)).line;
 
   @override
-  int get column => (_location ??= _sourceFile.getLocation(offset)).column;
+  int get column => (_location ??= _sourceFile!.getLocation(offset)).column;
 
   @override
-  String get sourceName;
+  String? get sourceName;
 
   @override
   String get shortText => '${sourceUri.pathSegments.last}:[$line,$column]';
@@ -415,34 +413,35 @@
 /// it is used to name the parameter stub for [element].
 // TODO(johnniwinther): Merge this with `computeKernelElementNameForSourceMaps`
 // when the old frontend is removed.
-String computeElementNameForSourceMaps(Entity element,
-    [CallStructure callStructure]) {
+String? computeElementNameForSourceMaps(Entity element,
+    [CallStructure? callStructure]) {
   if (element is ClassEntity) {
     return element.name;
-  } else if (element is MemberEntity) {
+  }
+  if (element is MemberEntity) {
+    final enclosingClass = element.enclosingClass;
     String suffix = computeStubSuffix(callStructure);
     if (element is ConstructorEntity || element is ConstructorBodyEntity) {
-      String className = element.enclosingClass.name;
+      String className = enclosingClass!.name;
       if (element.name == '') {
         return className;
       }
       return '$className.${element.name}$suffix';
-    } else if (element.enclosingClass != null) {
-      if (element.enclosingClass.isClosure) {
-        return computeElementNameForSourceMaps(
-            element.enclosingClass, callStructure);
-      }
-      return '${element.enclosingClass.name}.${element.name}$suffix';
-    } else {
-      return '${element.name}$suffix';
     }
+    if (enclosingClass != null) {
+      if (enclosingClass.isClosure) {
+        return computeElementNameForSourceMaps(enclosingClass, callStructure);
+      }
+      return '${enclosingClass.name}.${element.name}$suffix';
+    }
+    return '${element.name}$suffix';
   }
   // TODO(redemption): Create element names from kernel locals and closures.
   return element.name;
 }
 
 /// Compute the suffix used for a parameter stub for [callStructure].
-String computeStubSuffix(CallStructure callStructure) {
+String computeStubSuffix(CallStructure? callStructure) {
   if (callStructure == null) return '';
   StringBuffer sb = StringBuffer();
   sb.write(r'[function-entry$');
@@ -459,19 +458,19 @@
   const NoSourceLocationMarker();
 
   @override
-  Uri get sourceUri => null;
+  Uri? get sourceUri => null;
 
   @override
-  String get sourceName => null;
+  int get column => 0;
 
   @override
-  int get column => null;
+  int get line => 0;
 
   @override
-  int get line => null;
+  int get offset => 0;
 
   @override
-  int get offset => null;
+  String? get sourceName => null;
 
   String get shortName => '<no-location>';
 
@@ -486,10 +485,10 @@
 /// deobfuscate production stack traces.
 class FrameEntry {
   /// For push operations, the location of the inlining call, otherwise null.
-  final SourceLocation pushLocation;
+  final SourceLocation? pushLocation;
 
   /// For push operations, the inlined method name, otherwise null.
-  final String inlinedMethodName;
+  final String? inlinedMethodName;
 
   /// Whether a pop is the last pop that makes the inlining stack empty.
   final bool isEmptyPop;
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index 7e48ef3..349d065 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -2,15 +2,13 @@
 // 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.
 
-// @dart = 2.10
-
 library js;
 
 import 'package:js_ast/js_ast.dart';
 
 import '../common.dart';
 import '../options.dart';
-import '../dump_info.dart' show DumpInfoTask;
+import '../dump_info_javascript_monitor.dart' show DumpInfoJavaScriptMonitor;
 import '../io/code_output.dart' show CodeBuffer, CodeOutputListener;
 import 'js_source_mapping.dart';
 
@@ -35,7 +33,7 @@
 
 CodeBuffer createCodeBuffer(Node node, CompilerOptions compilerOptions,
     JavaScriptSourceInformationStrategy sourceInformationStrategy,
-    {DumpInfoTask monitor,
+    {DumpInfoJavaScriptMonitor? monitor,
     bool allowVariableMinification = true,
     List<CodeOutputListener> listeners = const []}) {
   JavaScriptPrintingOptions options = JavaScriptPrintingOptions(
@@ -55,7 +53,7 @@
 }
 
 class Dart2JSJavaScriptPrintingContext implements JavaScriptPrintingContext {
-  final DumpInfoTask monitor;
+  final DumpInfoJavaScriptMonitor? monitor;
   final CodeBuffer outBuffer;
   final CodePositionListener codePositionListener;
 
@@ -81,7 +79,7 @@
 
   @override
   void exitNode(
-      Node node, int startPosition, int endPosition, int closingPosition) {
+      Node node, int startPosition, int endPosition, int? closingPosition) {
     monitor?.exitNode(node, startPosition, endPosition, closingPosition);
     codePositionListener.onPositions(
         node, startPosition, endPosition, closingPosition);
@@ -135,7 +133,8 @@
   final Node tree;
   final bool _enableMinification;
   final bool _protectForEval;
-  LiteralString _cachedLiteral;
+  // TODO(48820): Can be `late final` with initializer.
+  LiteralString? _cachedLiteral;
 
   @override
   Iterable<Node> get containedNodes => [tree];
@@ -147,22 +146,20 @@
   /// parenthesis. The result is also escaped.
   UnparsedNode(this.tree, this._enableMinification, this._protectForEval);
 
-  LiteralString get _literal {
-    if (_cachedLiteral == null) {
-      String text = prettyPrint(tree, enableMinification: _enableMinification);
-      if (_protectForEval) {
-        if (tree is Fun) text = '($text)';
-        if (tree is LiteralExpression) {
-          LiteralExpression literalExpression = tree;
-          String template = literalExpression.template;
-          if (template.startsWith("function ") || template.startsWith("{")) {
-            text = '($text)';
-          }
+  LiteralString get _literal => _cachedLiteral ??= _create(tree);
+
+  LiteralString _create(Node node) {
+    String text = prettyPrint(node, enableMinification: _enableMinification);
+    if (_protectForEval) {
+      if (node is Fun) text = '($text)';
+      if (node is LiteralExpression) {
+        String template = node.template;
+        if (template.startsWith("function ") || template.startsWith("{")) {
+          text = '($text)';
         }
       }
-      _cachedLiteral = js.string(text);
     }
-    return _cachedLiteral;
+    return js.string(text);
   }
 
   @override
@@ -189,10 +186,9 @@
     node = call.target;
   }
   if (node is PropertyAccess) {
-    PropertyAccess access = node;
-    if (access.receiver is InterpolatedExpression) {
-      InterpolatedExpression hole = access.receiver;
-      return hole.isPositional && hole.nameOrPosition == 0;
+    final receiver = node.receiver;
+    if (receiver is InterpolatedExpression) {
+      return receiver.isPositional && receiver.nameOrPosition == 0;
     }
   }
   return false;
diff --git a/pkg/compiler/lib/src/js/js_source_mapping.dart b/pkg/compiler/lib/src/js/js_source_mapping.dart
index fc932c3..1dbd7b2 100644
--- a/pkg/compiler/lib/src/js/js_source_mapping.dart
+++ b/pkg/compiler/lib/src/js/js_source_mapping.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.10
-
 library js.source_mapping;
 
 import '../io/code_output.dart'
@@ -33,7 +31,8 @@
 class SourceInformationReader {
   const SourceInformationReader();
 
-  SourceInformation getSourceInformation(Node node) => node.sourceInformation;
+  SourceInformation? getSourceInformation(Node node) =>
+      node.sourceInformation as SourceInformation?;
 }
 
 /// An observer of code positions of printed JavaScript [Node]s.
@@ -50,7 +49,7 @@
   ///
   /// The nodes are seen in post-traversal order.
   void onPositions(
-      Node node, int startPosition, int endPosition, int closingPosition) {}
+      Node node, int startPosition, int endPosition, int? closingPosition) {}
 }
 
 /// Interface for creating [SourceMapper]s for multiple source information
@@ -79,7 +78,7 @@
 
   /// Associate [codeOffset] with an inlining call at [sourceLocation].
   void registerPush(
-      int codeOffset, SourceLocation sourceLocation, String inlinedMethodName);
+      int codeOffset, SourceLocation? sourceLocation, String inlinedMethodName);
 
   /// Associate [codeOffset] with an inlining return.
   ///
@@ -101,8 +100,8 @@
   }
 
   @override
-  void registerPush(
-      int codeOffset, SourceLocation sourceLocation, String inlinedMethodName) {
+  void registerPush(int codeOffset, SourceLocation? sourceLocation,
+      String inlinedMethodName) {
     sourceLocations.addPush(codeOffset, sourceLocation, inlinedMethodName);
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
index 22e8329..2e24fe1 100644
--- a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
+++ b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart
@@ -110,7 +110,7 @@
   @override
   js.Expression get value {
     assert(isFinalized, '$this is unassigned');
-    return _value;
+    return _value /*!*/;
   }
 
   @override
@@ -175,7 +175,7 @@
   }
 
   @override
-  Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
+  Iterable<js.Node> get containedNodes => isFinalized ? [value] : const [];
 }
 
 /// A [DeferredHolderParameter] is a deferred JavaScript expression determined
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 3c4f5fa..f49b6ce 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -514,7 +514,7 @@
   }
 
   Map<String, String> createMinifiedGlobalNameMap() {
-    var map = <String, String>{};
+    var map = <String, String /*!*/ >{};
     userGlobals.forEach((entity, jsName) {
       _registerName(map, jsName, entity.name);
     });
@@ -817,11 +817,11 @@
   }
 
   @override
-  jsAst.Name globalPropertyNameForMember(MemberEntity element) =>
+  jsAst.Name globalPropertyNameForMember(MemberEntity /*!*/ element) =>
       _disambiguateGlobalMember(element);
 
   @override
-  jsAst.Name globalPropertyNameForClass(ClassEntity element) =>
+  jsAst.Name globalPropertyNameForClass(ClassEntity /*!*/ element) =>
       _disambiguateGlobalType(element);
 
   @override
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index b5bf78d..a9db00c 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -408,7 +408,7 @@
   void writeMainFragment(MainFragment fragment, js.Statement code,
       {bool isSplit}) {
     LocationCollector locationCollector;
-    List<CodeOutputListener> codeOutputListeners;
+    List<CodeOutputListener /*!*/ > codeOutputListeners;
     if (_shouldGenerateSourceMap) {
       _task.measureSubtask('source-maps', () {
         locationCollector = LocationCollector();
@@ -478,7 +478,7 @@
       FinalizedFragment fragment,
       List<EmittedCodeFragment> fragmentCode,
       Map<CodeFragment, String> fragmentHashes) {
-    List<CodeOutputListener> outputListeners = [];
+    List<CodeOutputListener /*!*/ > outputListeners = [];
     LocationCollector locationCollector;
     if (_shouldGenerateSourceMap) {
       _task.measureSubtask('source-maps', () {
diff --git a/pkg/compiler/lib/src/serialization/serialization_interfaces.dart b/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
index efa9bb1..ccb047e 100644
--- a/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
+++ b/pkg/compiler/lib/src/serialization/serialization_interfaces.dart
@@ -76,9 +76,9 @@
   void writeTreeNodeMapInContext<V>(Map<ir.TreeNode, V>? map, void f(V value),
       {bool allowNull = false});
 
-  void writeCached<E extends Object>(E value, void f(E value));
+  void writeCached<E extends Object>(E? value, void f(E value));
 
-  void writeList<E extends Object>(Iterable<E> values, void f(E value),
+  void writeList<E extends Object>(Iterable<E>? values, void f(E value),
       {bool allowNull = false});
 }
 
@@ -117,6 +117,7 @@
   Map<K, V>? readTreeNodeMapInContextOrNull<K extends ir.TreeNode, V>(V f());
 
   E readCached<E extends Object>(E f());
+  E? readCachedOrNull<E extends Object>(E f());
 
   List<E> readList<E extends Object>(E f());
   List<E>? readListOrNull<E extends Object>(E f());
diff --git a/pkg/compiler/lib/src/serialization/sink.dart b/pkg/compiler/lib/src/serialization/sink.dart
index 214b6e9..8c5e1ba 100644
--- a/pkg/compiler/lib/src/serialization/sink.dart
+++ b/pkg/compiler/lib/src/serialization/sink.dart
@@ -113,7 +113,7 @@
   /// Writes a reference to [value] to this data sink. If [value] has not yet
   /// been serialized, [f] is called to serialize the value itself.
   @override
-  void writeCached<E>(E value, void f(E value)) {
+  void writeCached<E>(E /*?*/ value, void f(E value)) {
     IndexedSink sink = _generalCaches[E] ??= _createSink<E>();
     sink.write(value, (v) => f(v));
   }
diff --git a/pkg/compiler/lib/src/serialization/source.dart b/pkg/compiler/lib/src/serialization/source.dart
index 2d0855c..efe6512 100644
--- a/pkg/compiler/lib/src/serialization/source.dart
+++ b/pkg/compiler/lib/src/serialization/source.dart
@@ -173,6 +173,15 @@
   /// not yet been deserialized, [f] is called to deserialize the value itself.
   @override
   E readCached<E>(E f()) {
+    E /*?*/ value = readCachedOrNull(f);
+    if (value == null) throw StateError("Unexpected 'null' for $E");
+    return value;
+  }
+
+  /// Reads a reference to an [E] value from this data source. If the value has
+  /// not yet been deserialized, [f] is called to deserialize the value itself.
+  @override
+  E /*?*/ readCachedOrNull<E>(E f()) {
     IndexedSource source = _generalCaches[E] ??= _createSource<E>();
     return source.read(f);
   }
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 3755c74..b44fc3c 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -981,7 +981,8 @@
 
       if (ignoreAllocatorAnalysis ||
           !_fieldAnalysis.getFieldData(field).isInitializedInAllocator) {
-        if (node.initializer == null) {
+        final initializer = node.initializer;
+        if (initializer == null) {
           constructorData.fieldValues[field] =
               graph.addConstantNull(closedWorld);
         } else {
@@ -989,9 +990,10 @@
           // class type parameters are accessed as values.
           // TODO(sra): It would be sufficient to know the context was a field
           // initializer.
-          _inlinedFrom(field,
-              _sourceInformationBuilder.buildAssignment(node.initializer), () {
-            node.initializer.accept(this);
+          _inlinedFrom(
+              field, _sourceInformationBuilder.buildAssignment(initializer),
+              () {
+            initializer.accept(this);
             constructorData.fieldValues[field] = pop();
           });
         }
@@ -1183,7 +1185,7 @@
   }
 
   /// Constructs a special signature function for a closure.
-  void _buildMethodSignatureNewRti(ir.FunctionNode originalClosureNode) {
+  void _buildMethodSignatureNewRti(ir.FunctionNode /*!*/ originalClosureNode) {
     // The signature function has no corresponding ir.Node, so we just use the
     // targetElement to set up the type environment.
     _openFunction(targetElement, checks: TargetChecks.none);
@@ -1586,7 +1588,7 @@
   void _openFunction(MemberEntity member,
       {ir.FunctionNode functionNode,
       ParameterStructure parameterStructure,
-      TargetChecks checks}) {
+      /*required*/ TargetChecks checks}) {
     assert(checks != null);
 
     Map<Local, AbstractValue> parameterMap = {};
@@ -1640,6 +1642,8 @@
     // check. The null check is added before the argument type checks since in
     // strong mode, the parameter type might be non-nullable.
     if (member.name == '==') {
+      if (functionNode == null)
+        throw StateError("'==' should have functionNode");
       if (!_commonElements.operatorEqHandlesNullArgument(member)) {
         _handleIf(
             visitCondition: () {
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index 10d97e2..5dc3ae7 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -74,6 +74,12 @@
         help: 'The path to the package resolution configuration file, which '
             'supplies a mapping of package names\ninto paths.',
         hide: !verbose,
+      )
+      ..addOption(
+        'sdk-path',
+        valueHelp: 'path',
+        help: 'The path to the Dart SDK.',
+        hide: !verbose,
       );
   }
 
@@ -110,9 +116,30 @@
     var progress =
         machineFormat ? null : log.progress('Analyzing $targetsNames');
 
+    io.Directory sdkPath;
+    if (args.wasParsed('sdk-path')) {
+      sdkPath = io.Directory(args['sdk-path'] as String);
+      if (!sdkPath.existsSync()) {
+        usageException('Invalid Dart SDK path: $sdkPath');
+      }
+      final snapshotPath = path.join(
+        sdkPath.path,
+        'bin',
+        'snapshots',
+        'analysis_server.dart.snapshot',
+      );
+      if (!io.File(snapshotPath).existsSync()) {
+        usageException(
+            'Invalid Dart SDK path has no analysis_server.dart.snapshot file: '
+            '$sdkPath');
+      }
+    } else {
+      sdkPath = io.Directory(sdk.sdkPath);
+    }
+
     final AnalysisServer server = AnalysisServer(
       _packagesFile(),
-      io.Directory(sdk.sdkPath),
+      sdkPath,
       targets,
       cacheDirectoryPath: args['cache'],
       commandName: 'analyze',
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 2ca1456..0513849 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -5,6 +5,7 @@
 import 'package:cli_util/cli_logging.dart';
 import 'package:dartdev/src/analysis_server.dart';
 import 'package:dartdev/src/commands/analyze.dart';
+import 'package:dartdev/src/sdk.dart';
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -355,6 +356,25 @@
     expect(result.stdout, contains('1 issue found.'));
   });
 
+  test('--sdk-path value does not exist', () async {
+    p = project();
+    var result = await p.run(['analyze', '--sdk-path=bad']);
+
+    expect(result.exitCode, 64);
+    expect(result.stderr, contains('Invalid Dart SDK path: bad'));
+    expect(result.stderr, contains(_analyzeUsageText));
+  });
+
+  test('--sdk-path', () async {
+    var sdkPath = sdk.sdkPath;
+    p = project();
+    var result = await p.run(['analyze', '--sdk-path=$sdkPath']);
+
+    expect(result.exitCode, 0);
+    expect(result.stdout, contains('No issues found!'));
+    expect(result.stderr, isEmpty);
+  });
+
   test('--verbose', () async {
     p = project(mainSrc: '''
 int f() {
diff --git a/tools/VERSION b/tools/VERSION
index 33d92f0..525543e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 91
+PRERELEASE 92
 PRERELEASE_PATCH 0
\ No newline at end of file