[tfa,aot,dart2wasm] Allow tree-shaking of extension type members independently of their tear-offs

ExtensionTypeMemberDescriptor.memberReference and
ExtensionMemberDescriptor.memberReference are now nullable.

TFA-based tree shaker can now set them to null when extension type
member is not used and removed but corresponding lowered tear-off is
still used and retained.

Front-end never sets them to null and requires non-null memberReferences
when consuming kernel.

TEST=pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
Fixes https://github.com/flutter/flutter/issues/155624

Change-Id: I71f98c02f4659ff72a8c1d7fc6c578e8b8e26d82
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401382
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
index 21ddf2c..5fed2db 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
@@ -250,7 +250,7 @@
   bool get isSetter => kind == ExtensionMemberKind.Setter;
   bool get isMethod => kind == ExtensionMemberKind.Method;
 
-  bool get isExternal => (memberReference.asProcedure).isExternal;
+  bool get isExternal => (memberReference!.asProcedure).isExternal;
 }
 
 extension ProcedureExtension on Procedure {
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 03b8836..b70deea 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -981,7 +981,7 @@
     if (_processedExtensionLibraries.contains(library)) return;
     for (var extension in library.extensions) {
       for (var descriptor in extension.memberDescriptors) {
-        var reference = descriptor.memberReference;
+        var reference = descriptor.memberReference!;
         var onType = extension.onType;
         bool isInteropOnType = false;
         Annotatable? cls;
@@ -1127,7 +1127,7 @@
     for (var extensionType in library.extensionTypeDeclarations) {
       if (isInteropExtensionType(extensionType)) {
         for (var descriptor in extensionType.memberDescriptors) {
-          final reference = descriptor.memberReference;
+          final reference = descriptor.memberReference!;
           _extensionTypeMemberIndex[reference] = descriptor;
           _extensionTypeIndex[reference] = extensionType;
           final tearOffReference = descriptor.tearOffReference;
diff --git a/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart b/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
index 85f3fef..992d58b 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/shared_interop_transformer.dart
@@ -563,7 +563,7 @@
         break;
       default:
         for (final descriptor in interopTypeDecl.memberDescriptors) {
-          final descriptorNode = descriptor.memberReference.node;
+          final descriptorNode = descriptor.memberReference!.node;
           if (descriptorNode is Procedure &&
               _extensionIndex.isLiteralConstructor(descriptorNode)) {
             _diagnosticReporter.report(
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
index 7c2477f..9e25d2e 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
@@ -222,7 +222,7 @@
   // setters return their return and parameter types, respectively.
   DartType _getTypeOfDescriptor(ExtensionMemberDescriptor interopDescriptor) {
     // CFE creates static procedures for each extension member.
-    var interopMember = interopDescriptor.memberReference.asProcedure;
+    var interopMember = interopDescriptor.memberReference!.asProcedure;
 
     if (interopDescriptor.isGetter) {
       return interopMember.function.returnType;
@@ -365,7 +365,7 @@
               _descriptorToExtensionName[descriptor] =
                   extension.isUnnamedExtension ? '<unnamed>' : extension.name;
               var name = js_interop.getJSName(
-                descriptor.memberReference.asMember,
+                descriptor.memberReference!.asMember,
               );
               if (name.isEmpty) name = descriptor.name.text;
               exportNameToDescriptors!
diff --git a/pkg/dart2wasm/lib/record_class_generator.dart b/pkg/dart2wasm/lib/record_class_generator.dart
index d8ab225..90b6a2b 100644
--- a/pkg/dart2wasm/lib/record_class_generator.dart
+++ b/pkg/dart2wasm/lib/record_class_generator.dart
@@ -139,7 +139,7 @@
       .singleWhere((e) => e.name == 'WasmArrayExt')
       .memberDescriptors
       .singleWhere((member) => member.name.text == '[]')
-      .memberReference
+      .memberReference!
       .node as Procedure;
 
   late final Constructor wasmArrayLiteralConstructor =
diff --git a/pkg/front_end/lib/src/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
index 71eb09f..6aad377 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
@@ -42,14 +42,14 @@
       switch (descriptor.kind) {
         case ExtensionMemberKind.Method:
           if (descriptor.isStatic) {
-            Procedure procedure = descriptor.memberReference.asProcedure;
+            Procedure procedure = descriptor.memberReference!.asProcedure;
             nameSpace.addLocalMember(
                 name.text,
                 new DillExtensionStaticMethodBuilder(
                     procedure, descriptor, libraryBuilder, this),
                 setter: false);
           } else {
-            Procedure procedure = descriptor.memberReference.asProcedure;
+            Procedure procedure = descriptor.memberReference!.asProcedure;
             Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
             assert(tearOff != null, "No tear found for ${descriptor}");
             nameSpace.addLocalMember(
@@ -60,7 +60,7 @@
           }
           break;
         case ExtensionMemberKind.Getter:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionGetterBuilder(
@@ -68,7 +68,7 @@
               setter: false);
           break;
         case ExtensionMemberKind.Field:
-          Field field = descriptor.memberReference.asField;
+          Field field = descriptor.memberReference!.asField;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionFieldBuilder(
@@ -76,7 +76,7 @@
               setter: false);
           break;
         case ExtensionMemberKind.Setter:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionSetterBuilder(
@@ -84,7 +84,7 @@
               setter: true);
           break;
         case ExtensionMemberKind.Operator:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionOperatorBuilder(
diff --git a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
index a04f762..45eed1d 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
@@ -79,14 +79,14 @@
       switch (descriptor.kind) {
         case ExtensionTypeMemberKind.Method:
           if (descriptor.isStatic) {
-            Procedure procedure = descriptor.memberReference.asProcedure;
+            Procedure procedure = descriptor.memberReference!.asProcedure;
             nameSpace.addLocalMember(
                 name.text,
                 new DillExtensionTypeStaticMethodBuilder(
                     procedure, descriptor, libraryBuilder, this),
                 setter: false);
           } else {
-            Procedure procedure = descriptor.memberReference.asProcedure;
+            Procedure procedure = descriptor.memberReference!.asProcedure;
             Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
             assert(tearOff != null, "No tear found for ${descriptor}");
             nameSpace.addLocalMember(
@@ -97,7 +97,7 @@
           }
           break;
         case ExtensionTypeMemberKind.Getter:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionTypeGetterBuilder(
@@ -105,7 +105,7 @@
               setter: false);
           break;
         case ExtensionTypeMemberKind.Field:
-          Field field = descriptor.memberReference.asField;
+          Field field = descriptor.memberReference!.asField;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionTypeFieldBuilder(
@@ -113,7 +113,7 @@
               setter: false);
           break;
         case ExtensionTypeMemberKind.Setter:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionTypeSetterBuilder(
@@ -121,7 +121,7 @@
               setter: true);
           break;
         case ExtensionTypeMemberKind.Operator:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           nameSpace.addLocalMember(
               name.text,
               new DillExtensionTypeOperatorBuilder(
@@ -129,7 +129,7 @@
               setter: false);
           break;
         case ExtensionTypeMemberKind.Constructor:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
           nameSpace.addConstructor(
               name.text,
@@ -138,7 +138,7 @@
           break;
         case ExtensionTypeMemberKind.Factory:
         case ExtensionTypeMemberKind.RedirectingFactory:
-          Procedure procedure = descriptor.memberReference.asProcedure;
+          Procedure procedure = descriptor.memberReference!.asProcedure;
           Procedure? tearOff = descriptor.tearOffReference?.asProcedure;
           nameSpace.addConstructor(
               name.text,
diff --git a/pkg/front_end/lib/src/kernel/exhaustiveness.dart b/pkg/front_end/lib/src/kernel/exhaustiveness.dart
index e25a930..378e492 100644
--- a/pkg/front_end/lib/src/kernel/exhaustiveness.dart
+++ b/pkg/front_end/lib/src/kernel/exhaustiveness.dart
@@ -199,7 +199,7 @@
             fieldTypes[new NameKey(descriptor.name.text)] =
                 functionType.returnType;
           case ExtensionTypeMemberKind.Getter:
-            Procedure member = descriptor.memberReference.asProcedure;
+            Procedure member = descriptor.memberReference!.asProcedure;
             FunctionType functionType = member.getterType as FunctionType;
             if (extensionTypeDeclaration.typeParameters.isNotEmpty) {
               functionType = FunctionTypeInstantiator.instantiate(
diff --git a/pkg/front_end/lib/src/testing/id_testing_utils.dart b/pkg/front_end/lib/src/testing/id_testing_utils.dart
index ebbea44..9d6753c 100644
--- a/pkg/front_end/lib/src/testing/id_testing_utils.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_utils.dart
@@ -911,7 +911,8 @@
   }
 
   return [
-    descriptorToText(descriptor.memberReference, forTearOff: false),
+    if (descriptor.memberReference != null)
+      descriptorToText(descriptor.memberReference!, forTearOff: false),
     if (descriptor.tearOffReference != null)
       descriptorToText(descriptor.tearOffReference!, forTearOff: true),
   ];
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index ab9f04e..3f36b5a 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 121;
+  UInt32 formatVersion = 122;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -358,8 +358,8 @@
   Name name;
   ExtensionMemberKind kind;
   Byte flags (isStatic);
-  MemberReference member;
-  MemberReference tearOff;
+  MemberReference member;  // May be NullReference.
+  MemberReference tearOff; // May be NullReference.
 }
 
 type ExtensionTypeDeclaration extends Node {
@@ -384,8 +384,8 @@
   Name name;
   ExtensionTypeMemberKind kind;
   Byte flags (isStatic);
-  MemberReference member;
-  MemberReference tearOff;
+  MemberReference member;  // May be NullReference.
+  MemberReference tearOff; // May be NullReference.
 }
 
 abstract type Member extends Node {}
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index ca62b0e..4ef352d 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -1600,12 +1600,12 @@
     Name name = readName();
     int kind = readByte();
     int flags = readByte();
-    CanonicalName memberName = readNonNullCanonicalNameReference();
+    CanonicalName? memberName = readNullableCanonicalNameReference();
     CanonicalName? tearOffName = readNullableCanonicalNameReference();
     return new ExtensionMemberDescriptor(
         name: name,
         kind: ExtensionMemberKind.values[kind],
-        memberReference: memberName.reference,
+        memberReference: memberName?.reference,
         tearOffReference: tearOffName?.reference)
       ..flags = flags;
   }
@@ -1692,12 +1692,12 @@
     Name name = readName();
     int kind = readByte();
     int flags = readByte();
-    CanonicalName memberName = readNonNullCanonicalNameReference();
+    CanonicalName? memberName = readNullableCanonicalNameReference();
     CanonicalName? tearOffName = readNullableCanonicalNameReference();
     return new ExtensionTypeMemberDescriptor(
         name: name,
         kind: ExtensionTypeMemberKind.values[kind],
-        memberReference: memberName.reference,
+        memberReference: memberName?.reference,
         tearOffReference: tearOffName?.reference)
       ..flags = flags;
   }
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index c0315d4..0b4e4dc 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -2648,10 +2648,14 @@
       writeName(descriptor.name);
       writeByte(descriptor.kind.index);
       writeByte(descriptor.flags);
-      assert(descriptor.memberReference.canonicalName != null,
+      final Reference? memberReference = descriptor.memberReference;
+      assert(memberReference == null || memberReference.canonicalName != null,
           "No canonical name for ${descriptor}.");
-      writeNonNullCanonicalNameReference(descriptor.memberReference);
-      writeNullAllowedCanonicalNameReference(descriptor.tearOffReference);
+      writeNullAllowedCanonicalNameReference(memberReference);
+      final Reference? tearOffReference = descriptor.tearOffReference;
+      assert(tearOffReference == null || tearOffReference.canonicalName != null,
+          "No canonical name for ${descriptor} tear-off.");
+      writeNullAllowedCanonicalNameReference(tearOffReference);
     }
   }
 
@@ -2689,14 +2693,14 @@
       writeName(descriptor.name);
       writeByte(descriptor.kind.index);
       writeByte(descriptor.flags);
-      assert(descriptor.memberReference.canonicalName != null,
+      final Reference? memberReference = descriptor.memberReference;
+      assert(memberReference == null || memberReference.canonicalName != null,
           "No canonical name for ${descriptor}.");
-      writeNonNullCanonicalNameReference(descriptor.memberReference);
-      assert(
-          descriptor.tearOffReference == null ||
-              descriptor.tearOffReference?.canonicalName != null,
+      writeNullAllowedCanonicalNameReference(memberReference);
+      final Reference? tearOffReference = descriptor.tearOffReference;
+      assert(tearOffReference == null || tearOffReference.canonicalName != null,
           "No canonical name for ${descriptor} tear-off.");
-      writeNullAllowedCanonicalNameReference(descriptor.tearOffReference);
+      writeNullAllowedCanonicalNameReference(tearOffReference);
     }
   }
 
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 33ec5d2..a330cbc 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -226,7 +226,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 121;
+  static const int BinaryFormatVersion = 122;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/src/ast/declarations.dart b/pkg/kernel/lib/src/ast/declarations.dart
index 1867c62..8cd1e91 100644
--- a/pkg/kernel/lib/src/ast/declarations.dart
+++ b/pkg/kernel/lib/src/ast/declarations.dart
@@ -748,7 +748,9 @@
   int flags = 0;
 
   /// Reference to the top-level member created for the extension method.
-  final Reference memberReference;
+  /// This member reference is not null after the front-end but can
+  /// be cleared by certain back-ends (e.g. VM/AOT) if member is not used.
+  final Reference? memberReference;
 
   /// Reference to the top-level member created for the extension member tear
   /// off, if any.
@@ -761,6 +763,7 @@
       required this.memberReference,
       required this.tearOffReference}) {
     this.isStatic = isStatic;
+    assert(memberReference != null || tearOffReference != null);
   }
 
   /// Return `true` if the extension method was declared as `static`.
@@ -773,7 +776,7 @@
   @override
   String toString() {
     return 'ExtensionMemberDescriptor($name,$kind,'
-        '${memberReference.toStringInternal()},isStatic=${isStatic})';
+        '${memberReference?.toStringInternal()},isStatic=${isStatic})';
   }
 }
 
@@ -1034,7 +1037,9 @@
 
   /// Reference to the top-level member created for the extension type
   /// declaration member.
-  final Reference memberReference;
+  /// This member reference is not null after the front-end but can
+  /// be cleared by certain back-ends (e.g. VM/AOT) if member is not used.
+  final Reference? memberReference;
 
   /// Reference to the top-level member created for the extension type
   /// declaration member tear off, if any.
@@ -1047,6 +1052,7 @@
       required this.memberReference,
       required this.tearOffReference}) {
     this.isStatic = isStatic;
+    assert(memberReference != null || tearOffReference != null);
   }
 
   /// Return `true` if the extension type declaration member was declared as
@@ -1060,7 +1066,7 @@
   @override
   String toString() {
     return 'ExtensionTypeMemberDescriptor($name,$kind,'
-        '${memberReference.toStringInternal()},isStatic=${isStatic},'
+        '${memberReference?.toStringInternal()},isStatic=${isStatic},'
         '${tearOffReference?.toStringInternal()})';
   }
 }
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 8590c14..bebd9fa 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1307,9 +1307,13 @@
         endLine(';');
       }
 
-      writeReference(descriptor.memberReference, isTearOff: false);
-      if (descriptor.tearOffReference != null) {
-        writeReference(descriptor.tearOffReference!, isTearOff: true);
+      final Reference? memberReference = descriptor.memberReference;
+      if (memberReference != null) {
+        writeReference(memberReference, isTearOff: false);
+      }
+      final Reference? tearOffReference = descriptor.tearOffReference;
+      if (tearOffReference != null) {
+        writeReference(tearOffReference, isTearOff: true);
       }
     });
     --indentation;
@@ -1387,9 +1391,13 @@
         endLine(';');
       }
 
-      writeReference(descriptor.memberReference, isTearOff: false);
-      if (descriptor.tearOffReference != null) {
-        writeReference(descriptor.tearOffReference!, isTearOff: true);
+      final Reference? memberReference = descriptor.memberReference;
+      if (memberReference != null) {
+        writeReference(memberReference, isTearOff: false);
+      }
+      final Reference? tearOffReference = descriptor.tearOffReference;
+      if (tearOffReference != null) {
+        writeReference(tearOffReference, isTearOff: true);
       }
     });
     --indentation;
diff --git a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
index 97d9b59..023c423 100644
--- a/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
+++ b/pkg/kernel/lib/transformations/track_widget_constructor_locations.dart
@@ -716,8 +716,8 @@
         // there is a use case for this.
         continue;
       }
-      final Procedure method = member.memberReference.asProcedure;
-      if (_hasWidgetFactoryAnnotation(method)) {
+      final Procedure? method = member.memberReference?.asProcedure;
+      if (method != null && _hasWidgetFactoryAnnotation(method)) {
         _maybeAddNamedParameter(
           method.function,
           new VariableDeclaration(
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart
index ffbefa0..f3bbd3e 100644
--- a/pkg/kernel/lib/verifier.dart
+++ b/pkg/kernel/lib/verifier.dart
@@ -428,14 +428,16 @@
       for (Extension extension in library.extensions) {
         for (ExtensionMemberDescriptor descriptor
             in extension.memberDescriptors) {
-          Reference memberReference = descriptor.memberReference;
-          map[memberReference] = descriptor;
-          Member member = memberReference.asMember;
-          if (!member.isExtensionMember) {
-            problem(
-                member,
-                "Member $member (${descriptor}) from $extension is not marked "
-                "as an extension member.");
+          Reference? memberReference = descriptor.memberReference;
+          if (memberReference != null) {
+            map[memberReference] = descriptor;
+            Member member = memberReference.asMember;
+            if (!member.isExtensionMember) {
+              problem(
+                  member,
+                  "Member $member (${descriptor}) from $extension is not "
+                  " marked as an extension member.");
+            }
           }
           Reference? tearOffReference = descriptor.tearOffReference;
           if (tearOffReference != null) {
@@ -448,6 +450,12 @@
                   "marked as an extension member.");
             }
           }
+          if (memberReference == null && tearOffReference == null) {
+            problem(
+                extension,
+                "Both member and tear-off references are null in "
+                "the descriptor $descriptor from $extension.");
+          }
         }
       }
     }
@@ -463,14 +471,17 @@
           in library.extensionTypeDeclarations) {
         for (ExtensionTypeMemberDescriptor descriptor
             in extensionTypeDeclaration.memberDescriptors) {
-          Reference memberReference = descriptor.memberReference;
-          map[memberReference] = descriptor;
-          Member member = memberReference.asMember;
-          if (!member.isExtensionTypeMember) {
-            problem(
-                member,
-                "Member $member (${descriptor}) from $extensionTypeDeclaration "
-                "is not marked as an extension type member.");
+          Reference? memberReference = descriptor.memberReference;
+          if (memberReference != null) {
+            map[memberReference] = descriptor;
+            Member member = memberReference.asMember;
+            if (!member.isExtensionTypeMember) {
+              problem(
+                  member,
+                  "Member $member (${descriptor}) from "
+                  "$extensionTypeDeclaration is not marked as an extension "
+                  "type member.");
+            }
           }
           Reference? tearOffReference = descriptor.tearOffReference;
           if (tearOffReference != null) {
@@ -484,6 +495,12 @@
                   "type member.");
             }
           }
+          if (memberReference == null && tearOffReference == null) {
+            problem(
+                extensionTypeDeclaration,
+                "Both member and tear-off references are null in "
+                "the descriptor $descriptor from $extensionTypeDeclaration.");
+          }
         }
       }
     }
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index f92bd18..18d1031 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -959,7 +959,7 @@
         // The AST should have exactly one [Extension] for [m].
         final extension = m.enclosingLibrary.extensions.firstWhere((extension) {
           return extension.memberDescriptors.any((descriptor) =>
-              descriptor.memberReference.asMember == m ||
+              descriptor.memberReference?.asMember == m ||
               descriptor.tearOffReference?.asMember == m);
         });
 
@@ -976,7 +976,7 @@
             .enclosingLibrary.extensionTypeDeclarations
             .firstWhere((extensionTypeDeclaration) {
           return extensionTypeDeclaration.memberDescriptors.any((descriptor) =>
-              descriptor.memberReference.asMember == m ||
+              descriptor.memberReference?.asMember == m ||
               descriptor.tearOffReference?.asMember == m);
         });
 
@@ -2098,25 +2098,26 @@
         // To avoid depending on the order in which members and extensions are
         // visited during the transformation, we handle both cases: either the
         // member was already removed or it will be removed later.
-        final Reference memberReference = descriptor.memberReference;
-        final bool memberIsBound = memberReference.node != null;
+        final Reference? memberReference = descriptor.memberReference;
+        final bool memberIsBound = memberReference?.node != null;
         final bool isMemberUsed =
-            memberIsBound && shaker.isMemberUsed(memberReference.asMember);
+            memberIsBound && shaker.isMemberUsed(memberReference!.asMember);
         final Reference? tearOffReference = descriptor.tearOffReference;
         final bool tearOffIsBound = tearOffReference?.node != null;
         final bool isTearOffUsed =
             tearOffIsBound && shaker.isMemberUsed(tearOffReference!.asMember);
         if (isMemberUsed || isTearOffUsed) {
-          assert(!isTearOffUsed || isMemberUsed,
-              "Tear-off used without the member of $descriptor");
-          if (!isTearOffUsed) {
-            // Clear the tear-off reference since it is not used.
+          if ((!isMemberUsed && memberReference != null) ||
+              (!isTearOffUsed && tearOffReference != null)) {
+            // Clear references.
             descriptor = ExtensionMemberDescriptor(
                 name: descriptor.name,
                 kind: descriptor.kind,
                 isStatic: descriptor.isStatic,
-                memberReference: descriptor.memberReference,
-                tearOffReference: null);
+                memberReference:
+                    isMemberUsed ? descriptor.memberReference : null,
+                tearOffReference:
+                    isTearOffUsed ? descriptor.tearOffReference : null);
           }
           node.memberDescriptors[writeIndex++] = descriptor;
         }
@@ -2142,25 +2143,26 @@
         // declarations are visited during the transformation, we handle both
         // cases: either the member was already removed or it will be removed
         // later.
-        final Reference memberReference = descriptor.memberReference;
-        final bool memberIsBound = memberReference.node != null;
+        final Reference? memberReference = descriptor.memberReference;
+        final bool memberIsBound = memberReference?.node != null;
         final bool isMemberUsed =
-            memberIsBound && shaker.isMemberUsed(memberReference.asMember);
+            memberIsBound && shaker.isMemberUsed(memberReference!.asMember);
         final Reference? tearOffReference = descriptor.tearOffReference;
         final bool tearOffIsBound = tearOffReference?.node != null;
         final bool isTearOffUsed =
             tearOffIsBound && shaker.isMemberUsed(tearOffReference!.asMember);
         if (isMemberUsed || isTearOffUsed) {
-          assert(!isTearOffUsed || isMemberUsed,
-              "Tear-off used without the member of $descriptor");
-          if (!isTearOffUsed) {
-            // Clear the tear-off reference since it is not used.
+          if ((!isMemberUsed && memberReference != null) ||
+              (!isTearOffUsed && tearOffReference != null)) {
+            // Clear references.
             descriptor = ExtensionTypeMemberDescriptor(
                 name: descriptor.name,
                 kind: descriptor.kind,
                 isStatic: descriptor.isStatic,
-                memberReference: descriptor.memberReference,
-                tearOffReference: null);
+                memberReference:
+                    isMemberUsed ? descriptor.memberReference : null,
+                tearOffReference:
+                    isTearOffUsed ? descriptor.tearOffReference : null);
           }
           node.memberDescriptors[writeIndex++] = descriptor;
         }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
new file mode 100644
index 0000000..22ffc9f
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for https://github.com/flutter/flutter/issues/155624.
+// Verifies that TFA correctly tree shakes redirecting factory from
+// extension types when tear-off of the factory is used.
+
+extension type ET1(int id) {
+  ET1.c1() : this(0);
+  ET1.c2(this.id);
+  factory ET1.f1() = ET1.c1;
+  factory ET1.f2(int v) => ET1.c2(v);
+}
+
+void main() {
+  print(ET1.f1);
+  print(ET1.f2);
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect
new file mode 100644
index 0000000..fe436a3
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter155624.dart.expect
@@ -0,0 +1,60 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+
+extension type ET1(core::int id) {
+  constructor • = self::ET1|constructor#;
+  constructor c1 = self::ET1|constructor#c1;
+  constructor c2 = self::ET1|constructor#c2;
+  static redirecting-factory tearoff f1 = self::ET1|constructor#_#f1#tearOff;
+  static factory f2 = self::ET1|constructor#f2;
+  static factory tearoff f2 = self::ET1|constructor#_#f2#tearOff;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#() → self::ET1% /* erasure=core::int, declared=! */ {
+  lowered final self::ET1% /* erasure=core::int, declared=! */ #this = #C1;
+  return #this;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#c1() → self::ET1% /* erasure=core::int, declared=! */ {
+  lowered final self::ET1% /* erasure=core::int, declared=! */ #this;
+  #this = [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::ET1|constructor#();
+  return #this;
+}
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(i)->i]
+static extension-type-member method ET1|constructor#c2([@vm.inferred-arg-type.metadata=int] core::int id) → self::ET1% /* erasure=core::int, declared=! */ {
+  lowered final self::ET1% /* erasure=core::int, declared=! */ #this = id;
+  return #this;
+}
+
+[@vm.inferred-return-type.metadata=dart.core::_Smi (value: 0)]
+[@vm.unboxing-info.metadata=()->i]
+static extension-type-member method ET1|constructor#_#f1#tearOff() → self::ET1% /* erasure=core::int, declared=! */
+  return [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::ET1|constructor#c1();
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(i)->i]
+static extension-type-member method ET1|constructor#f2([@vm.inferred-arg-type.metadata=int] core::int v) → self::ET1% /* erasure=core::int, declared=! */
+  return [@vm.inferred-type.metadata=int] self::ET1|constructor#c2(v);
+
+[@vm.inferred-return-type.metadata=int]
+[@vm.unboxing-info.metadata=(b)->i]
+static extension-type-member method ET1|constructor#_#f2#tearOff(core::int v) → self::ET1% /* erasure=core::int, declared=! */
+  return [@vm.inferred-type.metadata=int] self::ET1|constructor#f2(v);
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+  core::print(#C2);
+  core::print(#C3);
+}
+constants  {
+  #C1 = 0
+  #C2 = static-tearoff self::ET1|constructor#_#f1#tearOff
+  #C3 = static-tearoff self::ET1|constructor#_#f2#tearOff
+}
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 879f6aa..9748a75 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -18,7 +18,7 @@
 // package:kernel/binary.md.
 
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
-static const uint32_t kSupportedKernelFormatVersion = 121;
+static const uint32_t kSupportedKernelFormatVersion = 122;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \