[dart2wasm] Make compiler use a compilation queue of compilation tasks

This creates a centralized loop that iterates over wasm functions to be
compiled. That centralized loop can then e.g. act on `--print-wasm`,
etc.

Migrate synthetic wasm functions to also use this compilation queue.

(We also remove the --export-all flag which is unused and untested)

Change-Id: I7a9384716c69ea2dd80424ff3dd7f736ac943f14
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/378703
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index 6d6d000..dab03be 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -9,6 +9,7 @@
 import 'closures.dart';
 import 'code_generator.dart';
 import 'state_machine.dart';
+import 'translator.dart' show CompilationTask;
 
 mixin AsyncCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
   late final ClassInfo asyncSuspendStateInfo =
@@ -74,9 +75,10 @@
     b.return_();
     b.end();
 
-    AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
-            functionNode, functionSource, closures)
-        .generate(resumeFun.locals.toList(), null);
+    translator.compilationQueue.add(CompilationTask(
+        resumeFun,
+        AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
+            functionNode, functionSource, closures)));
   }
 
   w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 1488cfe..04a64bb 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -31,10 +31,8 @@
   //   * doesn't inline (i.e. makes new function with this code) it will provide
   //     the parameters of the function and no return label.
   //
-  void generate(List<w.Local> paramLocals, w.Label? returnLabel);
-
-  // TODO(kustermann): Remove this again.
-  Closures get closures;
+  void generate(
+      w.InstructionsBuilder b, List<w.Local> paramLocals, w.Label? returnLabel);
 }
 
 /// Main code generator for member bodies.
@@ -58,10 +56,10 @@
     implements InitializerVisitor<void>, StatementVisitor<void>, CodeGenerator {
   final Translator translator;
   final w.FunctionType functionType;
-  final w.InstructionsBuilder b;
   final Member enclosingMember;
 
   // To be initialized in `generate()`
+  late final w.InstructionsBuilder b;
   late final List<w.Local> paramLocals;
   late final w.Label? returnLabel;
 
@@ -69,7 +67,6 @@
   late final StaticTypeContext typeContext =
       StaticTypeContext(enclosingMember, translator.typeEnvironment);
 
-  @override
   late final Closures closures;
 
   bool exceptionLocationPrinted = false;
@@ -103,8 +100,7 @@
       {};
 
   /// Create a code generator for a member or one of its lambdas.
-  AstCodeGenerator(
-      this.translator, this.functionType, this.b, this.enclosingMember);
+  AstCodeGenerator(this.translator, this.functionType, this.enclosingMember);
 
   w.ModuleBuilder get m => translator.m;
 
@@ -208,7 +204,9 @@
 
   /// Generate code while preventing recursive inlining.
   @override
-  void generate(List<w.Local> paramLocals, w.Label? returnLabel) {
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    this.b = b;
     this.paramLocals = paramLocals;
     this.returnLabel = returnLabel;
 
@@ -217,6 +215,16 @@
     translator.membersBeingGenerated.remove(enclosingMember);
   }
 
+  void addNestedClosuresToCompilationQueue() {
+    for (Lambda lambda in closures.lambdas.values) {
+      translator.compilationQueue.add(CompilationTask(
+          lambda.function,
+          getLambdaCodeGenerator(
+              translator, lambda, enclosingMember, closures)));
+    }
+  }
+
+  // Generate the body.
   void generateInternal();
 
   void _setupLocalParameters(Member member, ParameterInfo paramInfo,
@@ -651,7 +659,7 @@
     w.FunctionType targetFunctionType =
         translator.signatureForDirectCall(target);
     final inliningCodeGen =
-        translator.getInliningCodeGenerator(target, targetFunctionType, b);
+        translator.getInliningCodeGenerator(target, targetFunctionType);
     if (inliningCodeGen != null) {
       List<w.Local> inlinedLocals =
           targetFunctionType.inputs.map((t) => addLocal(t)).toList();
@@ -660,7 +668,7 @@
       }
       w.Label block = b.block(const [], targetFunctionType.outputs);
       b.comment("Inlined ${target.asMember}");
-      inliningCodeGen.generate(inlinedLocals, block);
+      inliningCodeGen.generate(b, inlinedLocals, block);
       return targetFunctionType.outputs;
     } else {
       w.BaseFunction targetFunction = translator.functions.getFunction(target);
@@ -3030,8 +3038,8 @@
     w.FunctionBuilder functionBuilder, Reference memberReference) {
   final member = memberReference.asMember;
   final asyncMarker = member.function?.asyncMarker ?? AsyncMarker.Sync;
-  final codeGen = getInlinableMemberCodeGenerator(translator, asyncMarker,
-      functionBuilder.type, functionBuilder.body, memberReference);
+  final codeGen = getInlinableMemberCodeGenerator(
+      translator, asyncMarker, functionBuilder.type, memberReference);
   if (codeGen != null) return codeGen;
 
   final procedure = member as Procedure;
@@ -3063,44 +3071,39 @@
 
 /// Returns a [CodeGenerator] for the given member iff that member can be
 /// inlined.
-CodeGenerator? getInlinableMemberCodeGenerator(
-    Translator translator,
-    AsyncMarker asyncMarker,
-    w.FunctionType functionType,
-    w.InstructionsBuilder b,
-    Reference reference) {
+CodeGenerator? getInlinableMemberCodeGenerator(Translator translator,
+    AsyncMarker asyncMarker, w.FunctionType functionType, Reference reference) {
   final Member member = reference.asMember;
 
   if (reference.isTearOffReference) {
-    return TearOffCodeGenerator(translator, functionType, b, member);
+    return TearOffCodeGenerator(translator, functionType, member);
   }
   if (reference.isTypeCheckerReference) {
-    return TypeCheckerCodeGenerator(translator, functionType, b, member);
+    return TypeCheckerCodeGenerator(translator, functionType, member);
   }
 
   if (member is Constructor) {
     if (reference.isConstructorBodyReference) {
-      return ConstructorCodeGenerator(translator, functionType, b, member);
+      return ConstructorCodeGenerator(translator, functionType, member);
     } else if (reference.isInitializerReference) {
-      return InitializerListCodeGenerator(translator, functionType, b, member);
+      return InitializerListCodeGenerator(translator, functionType, member);
     } else {
       return ConstructorAllocatorCodeGenerator(
-          translator, functionType, b, member);
+          translator, functionType, member);
     }
   }
 
   if (member is Field) {
     if (member.isStatic) {
       return StaticFieldInitializerCodeGenerator(
-          translator, functionType, b, member);
+          translator, functionType, member);
     }
     return ImplicitFieldAccessorCodeGenerator(
-        translator, functionType, b, member, reference.isImplicitGetter);
+        translator, functionType, member, reference.isImplicitGetter);
   }
 
   if (member is Procedure && asyncMarker == AsyncMarker.Sync) {
-    return SynchronousProcedureCodeGenerator(
-        translator, functionType, b, member);
+    return SynchronousProcedureCodeGenerator(translator, functionType, member);
   }
   assert(
       asyncMarker == AsyncMarker.SyncStar || asyncMarker == AsyncMarker.Async);
@@ -3110,18 +3113,15 @@
 class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
   final Procedure member;
 
-  SynchronousProcedureCodeGenerator(Translator translator,
-      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  SynchronousProcedureCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
     final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
     setSourceMapSourceAndFileOffset(source, member.fileOffset);
 
-    // Build closure information.
-    closures = Closures(translator, member);
-
     if (intrinsifier.generateMemberIntrinsic(
         member.reference, functionType, paramLocals, returnLabel)) {
       b.end();
@@ -3134,6 +3134,8 @@
       return;
     }
 
+    closures = Closures(translator, member);
+
     setupParametersAndContexts(member);
 
     Statement? body = member.function.body;
@@ -3143,27 +3145,21 @@
 
     _implicitReturn();
     b.end();
+    addNestedClosuresToCompilationQueue();
   }
 }
 
 class TearOffCodeGenerator extends AstCodeGenerator {
   final Member member;
 
-  TearOffCodeGenerator(Translator translator, w.FunctionType functionType,
-      w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  TearOffCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
-    if (member is Constructor) {
-      // Closures are built when constructor functions are added to worklist.
-      closures = translator.constructorClosures[member.reference]!;
-    } else {
-      // Build closure information.
-      closures = Closures(translator, member);
-    }
-
-    return generateTearOffGetter(member as Procedure);
+    closures = Closures(translator, member);
+    generateTearOffGetter(member as Procedure);
   }
 
   void generateTearOffGetter(Procedure procedure) {
@@ -3188,25 +3184,18 @@
 class TypeCheckerCodeGenerator extends AstCodeGenerator {
   final Member member;
 
-  TypeCheckerCodeGenerator(Translator translator, w.FunctionType functionType,
-      w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  TypeCheckerCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
-    if (member is Constructor) {
-      // Closures are built when constructor functions are added to worklist.
-      closures = translator.constructorClosures[member.reference]!;
-    } else {
-      // Build closure information.
-      closures = Closures(translator, member);
-    }
-
+    closures = Closures(translator, member);
     if (member is Field ||
         (member is Procedure && (member as Procedure).isSetter)) {
-      return _generateFieldSetterTypeCheckerMethod();
+      _generateFieldSetterTypeCheckerMethod();
     } else {
-      return _generateProcedureTypeCheckerMethod();
+      _generateProcedureTypeCheckerMethod();
     }
   }
 
@@ -3423,9 +3412,9 @@
 class InitializerListCodeGenerator extends AstCodeGenerator {
   final Constructor member;
 
-  InitializerListCodeGenerator(Translator translator,
-      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  InitializerListCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
@@ -3437,11 +3426,11 @@
 
     if (member.isExternal) {
       emitUnimplementedExternalError(member);
-      b.end();
-      return;
+    } else {
+      generateInitializerList();
     }
-
-    generateInitializerList();
+    b.end();
+    addNestedClosuresToCompilationQueue();
   }
 
   // Generates a constructor's initializer list method, and returns:
@@ -3475,7 +3464,6 @@
       // throws an error
       if (!containsSuperInitializer) {
         b.unreachable();
-        b.end();
         return;
       }
 
@@ -3484,7 +3472,6 @@
       for (Field field in info.cls!.fields) {
         if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
           b.unreachable();
-          b.end();
           return;
         }
       }
@@ -3509,8 +3496,6 @@
     for (w.Local field in initializedFields) {
       b.local_get(field);
     }
-
-    b.end();
   }
 
   void _setupInitializerListParametersAndContexts() {
@@ -3579,9 +3564,9 @@
 class ConstructorAllocatorCodeGenerator extends AstCodeGenerator {
   final Constructor member;
 
-  ConstructorAllocatorCodeGenerator(Translator translator,
-      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  ConstructorAllocatorCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
@@ -3692,13 +3677,12 @@
 class ConstructorCodeGenerator extends AstCodeGenerator {
   final Constructor member;
 
-  ConstructorCodeGenerator(Translator translator, w.FunctionType functionType,
-      w.InstructionsBuilder b, this.member)
-      : super(translator, functionType, b, member);
+  ConstructorCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.member)
+      : super(translator, functionType, member);
 
   @override
   void generateInternal() {
-    super.generate;
     // Closures are built when constructor functions are added to worklist.
     closures = translator.constructorClosures[member.reference]!;
 
@@ -3802,9 +3786,9 @@
 class StaticFieldInitializerCodeGenerator extends AstCodeGenerator {
   final Field field;
 
-  StaticFieldInitializerCodeGenerator(Translator translator,
-      w.FunctionType functionType, w.InstructionsBuilder b, this.field)
-      : super(translator, functionType, b, field);
+  StaticFieldInitializerCodeGenerator(
+      Translator translator, w.FunctionType functionType, this.field)
+      : super(translator, functionType, field);
 
   @override
   void generateInternal() {
@@ -3828,6 +3812,7 @@
     b.global_get(global);
     translator.convertType(b, global.type.type, outputs.single);
     b.end();
+    addNestedClosuresToCompilationQueue();
   }
 }
 
@@ -3838,15 +3823,12 @@
   ImplicitFieldAccessorCodeGenerator(
     Translator translator,
     w.FunctionType functionType,
-    w.InstructionsBuilder b,
     this.field,
     this.isImplicitGetter,
-  ) : super(translator, functionType, b, field);
+  ) : super(translator, functionType, field);
 
   @override
   void generateInternal() {
-    closures = Closures(translator, field);
-
     final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
     setSourceMapSourceAndFileOffset(source, field.fileOffset);
 
@@ -3885,8 +3867,7 @@
 
   SynchronousLambdaCodeGenerator(Translator translator, Member enclosingMember,
       this.lambda, this.enclosingMemberClosures)
-      : super(translator, lambda.function.type, lambda.function.body,
-            enclosingMember);
+      : super(translator, lambda.function.type, enclosingMember);
 
   @override
   void generateInternal() {
diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart
index 92a2886..0594156 100644
--- a/pkg/dart2wasm/lib/dart2wasm.dart
+++ b/pkg/dart2wasm/lib/dart2wasm.dart
@@ -18,8 +18,6 @@
 
 final List<Option> options = [
   Flag("help", (o, _) {}, abbr: "h", negatable: false, defaultsTo: false),
-  Flag("export-all", (o, value) => o.translatorOptions.exportAll = value,
-      defaultsTo: _d.translatorOptions.exportAll),
   Flag("import-shared-memory",
       (o, value) => o.translatorOptions.importSharedMemory = value,
       defaultsTo: _d.translatorOptions.importSharedMemory),
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 47fd222..24e1d59 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -7,13 +7,14 @@
 
 import 'class_info.dart';
 import 'closures.dart';
+import 'code_generator.dart';
 import 'dispatch_table.dart';
 import 'reference_extensions.dart';
 import 'translator.dart';
 
 /// This class is responsible for collecting import and export annotations.
-/// It also creates Wasm functions for Dart members and manages the worklist
-/// used to achieve tree shaking.
+/// It also creates Wasm functions for Dart members and manages the compilation
+/// queue used to achieve tree shaking.
 class FunctionCollector {
   final Translator translator;
 
@@ -21,14 +22,12 @@
   final Map<Reference, w.BaseFunction> _functions = {};
   // Names of exported functions
   final Map<Reference, String> _exports = {};
-  // Functions for which code has not yet been generated
-  final List<Reference> _worklist = [];
   // Selector IDs that are invoked via GDT.
   final Set<int> _calledSelectors = {};
   // Class IDs for classes that are allocated somewhere in the program
   final Set<int> _allocatedClasses = {};
-  // For each class ID, which functions should be added to the worklist if an
-  // allocation of that class is encountered
+  // For each class ID, which functions should be added to the compilation queue
+  // if an allocation of that class is encountered
   final Map<int, List<Reference>> _pendingAllocation = {};
 
   FunctionCollector(this.translator);
@@ -45,10 +44,6 @@
     }
   }
 
-  bool isWorkListEmpty() => _worklist.isEmpty;
-
-  Reference popWorkList() => _worklist.removeLast();
-
   void _importOrExport(Member member) {
     String? importName =
         translator.getPragma(member, "wasm:import", member.name.text);
@@ -97,19 +92,20 @@
   String? getExport(Reference target) => _exports[target];
 
   void initialize() {
-    // Add exports to the module and add exported functions to the worklist
+    // Add exports to the module and add exported functions to the compilationQueue
     for (var export in _exports.entries) {
       Reference target = export.key;
       Member node = target.asMember;
       if (node is Procedure) {
-        _worklist.add(target);
         assert(!node.isInstanceMember);
         assert(!node.isGetter);
         w.FunctionType ftype =
             _makeFunctionType(translator, target, null, isImportOrExport: true);
-        w.BaseFunction function = m.functions.define(ftype, "$node");
+        w.FunctionBuilder function = m.functions.define(ftype, "$node");
         _functions[target] = function;
         m.exports.export(export.value, function);
+        translator.compilationQueue.add(AstCompilationTask(function,
+            getMemberCodeGenerator(translator, function, target), target));
       } else if (node is Field) {
         w.Table? table = translator.getTable(node);
         if (table != null) {
@@ -133,9 +129,11 @@
 
   w.BaseFunction getFunction(Reference target) {
     return _functions.putIfAbsent(target, () {
-      _worklist.add(target);
-      return m.functions.define(
+      final function = m.functions.define(
           translator.signatureForDirectCall(target), _getFunctionName(target));
+      translator.compilationQueue.add(AstCompilationTask(function,
+          getMemberCodeGenerator(translator, function, target), target));
+      return function;
     });
   }
 
diff --git a/pkg/dart2wasm/lib/state_machine.dart b/pkg/dart2wasm/lib/state_machine.dart
index 1a93fa8..ee36203 100644
--- a/pkg/dart2wasm/lib/state_machine.dart
+++ b/pkg/dart2wasm/lib/state_machine.dart
@@ -567,7 +567,7 @@
   final w.FunctionBuilder function;
   StateMachineEntryAstCodeGenerator(
       Translator translator, Member enclosingMember, this.function)
-      : super(translator, function.type, function.body, enclosingMember);
+      : super(translator, function.type, enclosingMember);
 
   /// Generate the outer function.
   ///
@@ -601,6 +601,7 @@
     if (context != null && context.isEmpty) context = context.parent;
 
     generateOuter(member.function, context, source);
+    addNestedClosuresToCompilationQueue();
   }
 }
 
@@ -645,7 +646,7 @@
       this.functionNode,
       this.functionSource,
       Closures closures)
-      : super(translator, function.type, function.body, enclosingMember) {
+      : super(translator, function.type, enclosingMember) {
     this.closures = closures;
   }
 
diff --git a/pkg/dart2wasm/lib/sync_star.dart b/pkg/dart2wasm/lib/sync_star.dart
index 9a9457b..193f562 100644
--- a/pkg/dart2wasm/lib/sync_star.dart
+++ b/pkg/dart2wasm/lib/sync_star.dart
@@ -9,6 +9,7 @@
 import 'closures.dart';
 import 'code_generator.dart';
 import 'state_machine.dart';
+import 'translator.dart' show CompilationTask;
 
 mixin SyncStarCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
   late final ClassInfo suspendStateInfo =
@@ -40,9 +41,10 @@
     b.return_();
     b.end();
 
-    SyncStarStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
-            functionNode, functionSource, closures)
-        .generate(resumeFun.locals.toList(), null);
+    translator.compilationQueue.add(CompilationTask(
+        resumeFun,
+        SyncStarStateMachineCodeGenerator(translator, resumeFun,
+            enclosingMember, functionNode, functionSource, closures)));
   }
 
   w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index b1d797c..37215a1 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -31,7 +31,6 @@
 /// Options controlling the translation.
 class TranslatorOptions {
   bool enableAsserts = false;
-  bool exportAll = false;
   bool importSharedMemory = false;
   bool inlining = true;
   bool jsCompatibility = false;
@@ -101,6 +100,7 @@
   late final Globals globals;
   late final Constants constants;
   late final Types types;
+  late final CompilationQueue compilationQueue;
   late final FunctionCollector functions;
   late final DynamicForwarders dynamicForwarders;
 
@@ -133,7 +133,6 @@
   final Set<Member> membersContainingInnerFunctions = {};
   final Set<Member> membersBeingGenerated = {};
   final Map<Reference, Closures> constructorClosures = {};
-  final List<_FunctionGenerator> _pendingFunctions = [];
   late final w.ModuleBuilder m;
   late final w.FunctionBuilder initFunction;
   late final w.ValueType voidMarker;
@@ -288,6 +287,7 @@
     closureLayouter = ClosureLayouter(this);
     classInfoCollector = ClassInfoCollector(this);
     dispatchTable = DispatchTable(this);
+    compilationQueue = CompilationQueue();
     functions = FunctionCollector(this);
     types = Types(this);
     dynamicForwarders = DynamicForwarders(this);
@@ -314,91 +314,9 @@
     dispatchTable.build();
 
     functions.initialize();
-    while (!functions.isWorkListEmpty()) {
-      Reference reference = functions.popWorkList();
-      Member member = reference.asMember;
-      var function = functions.getExistingFunction(reference) as w.BaseFunction;
-
-      String canonicalName = "$member";
-      if (reference.isSetter) {
-        canonicalName = "$canonicalName=";
-      } else if (reference.isGetter || reference.isTearOffReference) {
-        int dot = canonicalName.indexOf('.');
-        canonicalName =
-            '${canonicalName.substring(0, dot + 1)}=${canonicalName.substring(dot + 1)}';
-      }
-      canonicalName = member.enclosingLibrary == libraries.first
-          ? canonicalName
-          : "${member.enclosingLibrary.importUri} $canonicalName";
-
-      String? exportName = functions.getExport(reference);
-
-      if (options.printKernel || options.printWasm) {
-        String header = "#${function.name}: $canonicalName";
-        if (exportName != null) {
-          header = "$header (exported as $exportName)";
-        }
-        if (reference.isTypeCheckerReference) {
-          header = "$header (type checker)";
-        }
-        print(header);
-        print(member.function
-            ?.computeFunctionType(Nullability.nonNullable)
-            .toStringInternal());
-      }
-      if (options.printKernel && !reference.isTypeCheckerReference) {
-        if (member is Constructor) {
-          Class cls = member.enclosingClass;
-          for (Field field in cls.fields) {
-            if (field.isInstanceMember && field.initializer != null) {
-              print("${field.name}: ${field.initializer}");
-            }
-          }
-          for (Initializer initializer in member.initializers) {
-            print(initializer);
-          }
-        }
-        Statement? body = member.function?.body;
-        if (body != null) {
-          print(body);
-        }
-        if (!options.printWasm) print("");
-      }
-
-      if (options.exportAll && exportName == null) {
-        m.exports.export(canonicalName, function);
-      }
-
-      final functionBuilder = function as w.FunctionBuilder;
-      final codeGen = getMemberCodeGenerator(this, functionBuilder, reference);
-      codeGen.generate(functionBuilder.locals.toList(), null);
-      final memberClosures = codeGen.closures;
-
-      if (options.printWasm) {
-        print(function.type);
-        print(function.body.trace);
-      }
-
-      // The constructor allocator, initializer, and body functions all
-      // share the same Closures, which will contain all the lambdas in the
-      // constructor initializer and body. But, we only want to generate
-      // the lambda functions once, so we only generate lambdas when the
-      // constructor initializer methods are generated.
-      if (member is! Constructor || reference.isInitializerReference) {
-        final enclosingMember = member;
-        for (Lambda lambda in memberClosures.lambdas.values) {
-          getLambdaCodeGenerator(this, lambda, enclosingMember, memberClosures)
-              .generate(lambda.function.locals.toList(), null);
-          _printFunction(function, "$canonicalName (closure)");
-        }
-      }
-
-      // Use an indexed loop to handle pending closure trampolines, since new
-      // entries might be added during iteration.
-      for (int i = 0; i < _pendingFunctions.length; i++) {
-        _pendingFunctions[i].generate(this);
-      }
-      _pendingFunctions.clear();
+    while (!compilationQueue.isEmpty) {
+      final task = compilationQueue.pop();
+      task.run(this, options.printKernel, options.printWasm);
     }
 
     constructorClosures.clear();
@@ -759,26 +677,20 @@
     w.BaseFunction makeTrampoline(
         w.FunctionType signature, int posArgCount, List<String> argNames) {
       final trampoline = m.functions.define(signature, "$name trampoline");
-
-      // Defer generation of the trampoline body to avoid cyclic dependency
-      // when a tear-off constant is used as default value in the torn-off
-      // function.
-      _pendingFunctions.add(_ClosureTrampolineGenerator(trampoline, target,
-          typeCount, posArgCount, argNames, paramInfo, takesContextOrReceiver));
-
+      compilationQueue.add(CompilationTask(
+          trampoline,
+          _ClosureTrampolineGenerator(this, trampoline, target, typeCount,
+              posArgCount, argNames, paramInfo, takesContextOrReceiver)));
       return trampoline;
     }
 
     w.BaseFunction makeDynamicCallEntry() {
       final function = m.functions.define(
           dynamicCallVtableEntryFunctionType, "$name dynamic call entry");
-
-      // Defer generation of the trampoline body to avoid cyclic dependency
-      // when a tear-off constant is used as default value in the torn-off
-      // function.
-      _pendingFunctions.add(_ClosureDynamicEntryGenerator(
-          functionNode, target, paramInfo, name, function));
-
+      compilationQueue.add(CompilationTask(
+          function,
+          _ClosureDynamicEntryGenerator(
+              this, functionNode, target, paramInfo, name, function)));
       return function;
     }
 
@@ -1087,10 +999,10 @@
   }
 
   CodeGenerator? getInliningCodeGenerator(
-      Reference target, w.FunctionType functionType, w.InstructionsBuilder b) {
+      Reference target, w.FunctionType functionType) {
     if (!shouldInline(target)) return null;
     return getInlinableMemberCodeGenerator(
-        this, AsyncMarker.Sync, functionType, b, target);
+        this, AsyncMarker.Sync, functionType, target);
   }
 
   T? getPragma<T>(Annotatable node, String name, [T? defaultValue]) {
@@ -1190,11 +1102,108 @@
   }
 }
 
-abstract class _FunctionGenerator {
-  void generate(Translator translator);
+class CompilationQueue {
+  final List<CompilationTask> _pending = [];
+
+  bool get isEmpty => _pending.isEmpty;
+  void add(CompilationTask entry) => _pending.add(entry);
+  CompilationTask pop() => _pending.removeLast();
 }
 
-class _ClosureTrampolineGenerator implements _FunctionGenerator {
+class CompilationTask {
+  final w.FunctionBuilder function;
+  final CodeGenerator _codeGenerator;
+
+  CompilationTask(this.function, this._codeGenerator);
+
+  void run(Translator translator, bool printKernel, bool printWasm) {
+    _codeGenerator.generate(function.body, function.locals.toList(), null);
+    if (printWasm) {
+      print("#${function.name} (synthetic)");
+      print(function.type);
+      print(function.body.trace);
+    }
+  }
+}
+
+// Compilation task for AST.
+class AstCompilationTask extends CompilationTask {
+  final Reference reference;
+
+  AstCompilationTask(super.function, super._createCodeGenerator, this.reference)
+      : super();
+
+  @override
+  void run(Translator translator, bool printKernel, bool printWasm) {
+    final member = reference.asMember;
+
+    final codeGen = getMemberCodeGenerator(translator, function, reference);
+    codeGen.generate(function.body, function.locals.toList(), null);
+
+    if (printKernel || printWasm) {
+      final (:name, :exportName) = _getNames(translator);
+
+      String header = "#${function.name}: $name";
+      if (exportName != null) {
+        header = "$header (exported as $exportName)";
+      }
+      if (reference.isTypeCheckerReference) {
+        header = "$header (type checker)";
+      }
+      print(header);
+      print(member.function
+          ?.computeFunctionType(Nullability.nonNullable)
+          .toStringInternal());
+    }
+    if (printKernel && !reference.isTypeCheckerReference) {
+      if (member is Constructor) {
+        Class cls = member.enclosingClass;
+        for (Field field in cls.fields) {
+          if (field.isInstanceMember && field.initializer != null) {
+            print("${field.name}: ${field.initializer}");
+          }
+        }
+        for (Initializer initializer in member.initializers) {
+          print(initializer);
+        }
+      }
+      Statement? body = member.function?.body;
+      if (body != null) {
+        print(body);
+      }
+      if (!printWasm) print("");
+    }
+
+    if (printWasm) {
+      print(function.type);
+      print(function.body.trace);
+    }
+  }
+
+  ({String name, String? exportName}) _getNames(Translator translator) {
+    final member = reference.asMember;
+    String canonicalName = "$member";
+    if (reference.isSetter) {
+      canonicalName = "$canonicalName=";
+    } else if (reference.isGetter || reference.isTearOffReference) {
+      int dot = canonicalName.indexOf('.');
+      canonicalName =
+          '${canonicalName.substring(0, dot + 1)}=${canonicalName.substring(dot + 1)}';
+    }
+    canonicalName = member.enclosingLibrary ==
+            translator.component.mainMethod!.enclosingLibrary
+        ? canonicalName
+        : "${member.enclosingLibrary.importUri} $canonicalName";
+
+    return (
+      name: canonicalName,
+      exportName: translator.functions.getExport(reference)
+    );
+  }
+}
+
+class _ClosureTrampolineGenerator implements CodeGenerator {
+  final Translator translator;
   final w.FunctionBuilder trampoline;
   final w.BaseFunction target;
   final int typeCount;
@@ -1204,6 +1213,7 @@
   final bool takesContextOrReceiver;
 
   _ClosureTrampolineGenerator(
+      this.translator,
       this.trampoline,
       this.target,
       this.typeCount,
@@ -1213,8 +1223,10 @@
       this.takesContextOrReceiver);
 
   @override
-  void generate(Translator translator) {
-    final b = trampoline.body;
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null);
+
     int targetIndex = 0;
     if (takesContextOrReceiver) {
       w.Local receiver = trampoline.locals[0];
@@ -1264,18 +1276,22 @@
 
 /// Similar to [_ClosureTrampolineGenerator], but generates dynamic call
 /// entries.
-class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
+class _ClosureDynamicEntryGenerator implements CodeGenerator {
+  final Translator translator;
   final FunctionNode functionNode;
   final w.BaseFunction target;
   final ParameterInfo paramInfo;
   final String name;
   final w.FunctionBuilder function;
 
-  _ClosureDynamicEntryGenerator(
-      this.functionNode, this.target, this.paramInfo, this.name, this.function);
+  _ClosureDynamicEntryGenerator(this.translator, this.functionNode, this.target,
+      this.paramInfo, this.name, this.function);
 
   @override
-  void generate(Translator translator) {
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null);
+
     final b = function.body;
 
     final bool takesContextOrReceiver =
@@ -1550,46 +1566,62 @@
     return cache.putIfAbsent(selector, () {
       final name = '${selector.name} (polymorphic dispatcher)';
       final signature = selector.signature;
-      final inputs = signature.inputs;
-      final outputs = signature.outputs;
-      final function = translator.m.functions
-          .define(translator.m.types.defineFunction(inputs, outputs), name);
+      final function = translator.m.functions.define(
+          translator.m.types
+              .defineFunction(signature.inputs, signature.outputs),
+          name);
 
-      final b = function.body;
-
-      final targetRanges = selector.staticDispatchRanges
-          .map((entry) => (range: entry.range, value: entry.target))
-          .toList();
-
-      final bool needFallback =
-          selector.targetRanges.length > selector.staticDispatchRanges.length;
-      void emitDirectCall(Reference target) {
-        for (int i = 0; i < inputs.length; ++i) {
-          b.local_get(b.locals[i]);
-        }
-        b.call(translator.functions.getFunction(target));
-      }
-
-      void emitDispatchTableCall() {
-        for (int i = 0; i < inputs.length; ++i) {
-          b.local_get(b.locals[i]);
-        }
-        b.local_get(b.locals[0]);
-        b.struct_get(translator.topInfo.struct, FieldIndex.classId);
-        b.i32_const(selector.offset!);
-        b.i32_add();
-        b.call_indirect(signature, translator.dispatchTable.wasmTable);
-        translator.functions.recordSelectorUse(selector);
-      }
-
-      b.local_get(b.locals[0]);
-      b.struct_get(translator.topInfo.struct, FieldIndex.classId);
-      b.classIdSearch(targetRanges, outputs, emitDirectCall,
-          needFallback ? emitDispatchTableCall : null);
-      b.return_();
-      b.end();
+      translator.compilationQueue.add(CompilationTask(function,
+          PolymorphicDispatcherCodeGenerator(translator, signature, selector)));
 
       return function;
     });
   }
 }
+
+class PolymorphicDispatcherCodeGenerator implements CodeGenerator {
+  final Translator translator;
+  final w.FunctionType signature;
+  final SelectorInfo selector;
+
+  PolymorphicDispatcherCodeGenerator(
+      this.translator, this.signature, this.selector);
+
+  @override
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null);
+
+    final targetRanges = selector.staticDispatchRanges
+        .map((entry) => (range: entry.range, value: entry.target))
+        .toList();
+
+    final bool needFallback =
+        selector.targetRanges.length > selector.staticDispatchRanges.length;
+    void emitDirectCall(Reference target) {
+      for (int i = 0; i < signature.inputs.length; ++i) {
+        b.local_get(b.locals[i]);
+      }
+      b.call(translator.functions.getFunction(target));
+    }
+
+    void emitDispatchTableCall() {
+      for (int i = 0; i < signature.inputs.length; ++i) {
+        b.local_get(b.locals[i]);
+      }
+      b.local_get(b.locals[0]);
+      b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+      b.i32_const(selector.offset!);
+      b.i32_add();
+      b.call_indirect(signature, translator.dispatchTable.wasmTable);
+      translator.functions.recordSelectorUse(selector);
+    }
+
+    b.local_get(b.locals[0]);
+    b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+    b.classIdSearch(targetRanges, signature.outputs, emitDirectCall,
+        needFallback ? emitDispatchTableCall : null);
+    b.return_();
+    b.end();
+  }
+}
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 4bdd5b4..005b7b8 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -604,95 +604,10 @@
           ),
           name);
 
-      final b = function.body;
-
-      w.Local operand = b.locals[0];
-      w.Local boolTemp = b.addLocal(w.NumType.i32);
-
-      final w.Label resultLabel = b.block(const [], const [w.NumType.i32]);
-      if (operandIsNullable) {
-        w.Label nullLabel = b.block(const [], const []);
-        b.local_get(operand);
-        b.br_on_null(nullLabel);
-        final nonNullableOperand =
-            b.addLocal(translator.topInfo.nonNullableType);
-        b.local_get(operand);
-        b.ref_cast(nonNullableOperand.type as w.RefType);
-        b.local_set(nonNullableOperand);
-        operand = nonNullableOperand;
-      }
-
-      if (checkArguments) {
-        b.local_get(operand);
-        b.call(_generateIsChecker(testedAgainstType, false, false));
-        b.local_set(boolTemp);
-
-        // If cid ranges fail, we fail
-        {
-          final w.Label okBlock = b.block(const [], const []);
-          b.local_get(boolTemp);
-          b.i32_const(1);
-          b.i32_eq();
-          b.br_if(okBlock);
-          b.i32_const(0);
-          b.br(resultLabel);
-          b.end();
-        }
-
-        // Otherwise we have to check each argument.
-
-        // Call Object._getArguments()
-        w.Local typeArguments = b.addLocal(typeArrayExpectedType);
-        b.local_get(operand);
-        b.call(translator.functions
-            .getFunction(translator.objectGetTypeArguments.reference));
-        b.local_set(typeArguments);
-        for (int i = 0; i < argumentCount; ++i) {
-          b.local_get(typeArguments);
-          b.i32_const(i);
-          b.array_get(typeArrayArrayType);
-          b.local_get(b.locals[1 + i]);
-          b.call(translator.functions
-              .getFunction(translator.isTypeSubtype.reference));
-          {
-            b.local_set(boolTemp);
-            final w.Label okBlock = b.block(const [], const []);
-            b.local_get(boolTemp);
-            b.i32_const(1);
-            b.i32_eq();
-            b.br_if(okBlock);
-            b.i32_const(0);
-            b.br(resultLabel);
-            b.end();
-          }
-        }
-        b.i32_const(1);
-        b.br(resultLabel);
-      } else {
-        if (interfaceClass == coreTypes.objectClass) {
-          b.drop();
-          b.i32_const(1);
-        } else if (interfaceClass == coreTypes.functionClass) {
-          b.local_get(operand);
-          b.ref_test(translator.closureInfo.nonNullableType);
-        } else {
-          final ranges = translator.classIdNumbering
-              .getConcreteClassIdRanges(interfaceClass);
-          b.local_get(operand);
-          b.struct_get(translator.topInfo.struct, FieldIndex.classId);
-          b.emitClassIdRangeCheck(ranges);
-        }
-        b.br(resultLabel);
-      }
-
-      if (operandIsNullable) {
-        b.end(); // nullLabel
-        b.i32_const(encodedNullability(testedAgainstType));
-      }
-      b.end(); // resultLabel
-
-      b.return_();
-      b.end();
+      translator.compilationQueue.add(CompilationTask(
+          function,
+          IsCheckerCodeGenerator(translator, testedAgainstType,
+              operandIsNullable, checkArguments, argumentCount)));
 
       return function;
     });
@@ -741,61 +656,209 @@
           ),
           name);
 
-      final b = function.body;
-      w.Label asCheckBlock = b.block();
-      b.local_get(b.locals[0]);
-      for (int i = 0; i < argumentCount; ++i) {
-        b.local_get(b.locals[1 + i]);
-      }
-      b.call(_generateIsChecker(
-          testedAgainstType, checkArguments, operandIsNullable));
-      b.br_if(asCheckBlock);
-
-      if (checkArguments) {
-        final testedAgainstClassId =
-            translator.classInfo[testedAgainstType.classNode]!.classId;
-        b.local_get(b.locals[0]);
-        b.i32_const(encodedNullability(testedAgainstType));
-        b.i32_const(testedAgainstClassId);
-        if (argumentCount == 1) {
-          b.local_get(b.locals[1]);
-          b.call(translator.functions.getFunction(
-              translator.throwInterfaceTypeAsCheckError1.reference));
-        } else if (argumentCount == 2) {
-          b.local_get(b.locals[1]);
-          b.local_get(b.locals[2]);
-          b.call(translator.functions.getFunction(
-              translator.throwInterfaceTypeAsCheckError2.reference));
-        } else {
-          for (int i = 0; i < argumentCount; ++i) {
-            b.local_get(b.locals[1 + i]);
-          }
-          b.array_new_fixed(typeArrayArrayType, argumentCount);
-          b.call(translator.functions.getFunction(
-              translator.throwInterfaceTypeAsCheckError.reference));
-        }
-      } else {
-        b.local_get(b.locals[0]);
-        translator.constants.instantiateConstant(
-            b, TypeLiteralConstant(testedAgainstType), nonNullableTypeType);
-        b.call(translator.functions
-            .getFunction(translator.throwAsCheckError.reference));
-      }
-      b.unreachable();
-
-      b.end();
-
-      b.local_get(b.locals[0]);
-      translator.convertType(b, argumentType, returnType);
-      b.return_();
-      b.end();
+      translator.compilationQueue.add(CompilationTask(
+          function,
+          AsCheckerCodeGenerator(
+              translator,
+              argumentType,
+              returnType,
+              testedAgainstType,
+              operandIsNullable,
+              checkArguments,
+              argumentCount)));
 
       return function;
     });
   }
+}
 
-  int encodedNullability(DartType type) =>
-      type.declaredNullability == Nullability.nullable ? 1 : 0;
+int encodedNullability(DartType type) =>
+    type.declaredNullability == Nullability.nullable ? 1 : 0;
+
+class IsCheckerCodeGenerator implements CodeGenerator {
+  final Translator translator;
+
+  final InterfaceType testedAgainstType;
+  final bool operandIsNullable;
+  final bool checkArguments;
+  final int argumentCount;
+
+  IsCheckerCodeGenerator(this.translator, this.testedAgainstType,
+      this.operandIsNullable, this.checkArguments, this.argumentCount);
+
+  @override
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null);
+
+    final interfaceClass = testedAgainstType.classNode;
+
+    w.Local operand = b.locals[0];
+    w.Local boolTemp = b.addLocal(w.NumType.i32);
+
+    final w.Label resultLabel = b.block(const [], const [w.NumType.i32]);
+    if (operandIsNullable) {
+      w.Label nullLabel = b.block(const [], const []);
+      b.local_get(operand);
+      b.br_on_null(nullLabel);
+      final nonNullableOperand = b.addLocal(translator.topInfo.nonNullableType);
+      b.local_get(operand);
+      b.ref_cast(nonNullableOperand.type as w.RefType);
+      b.local_set(nonNullableOperand);
+      operand = nonNullableOperand;
+    }
+
+    if (checkArguments) {
+      b.local_get(operand);
+      b.call(
+          translator.types._generateIsChecker(testedAgainstType, false, false));
+      b.local_set(boolTemp);
+
+      // If cid ranges fail, we fail
+      {
+        final w.Label okBlock = b.block(const [], const []);
+        b.local_get(boolTemp);
+        b.i32_const(1);
+        b.i32_eq();
+        b.br_if(okBlock);
+        b.i32_const(0);
+        b.br(resultLabel);
+        b.end();
+      }
+
+      // Otherwise we have to check each argument.
+
+      // Call Object._getArguments()
+      w.Local typeArguments =
+          b.addLocal(translator.types.typeArrayExpectedType);
+      b.local_get(operand);
+      b.call(translator.functions
+          .getFunction(translator.objectGetTypeArguments.reference));
+      b.local_set(typeArguments);
+      for (int i = 0; i < argumentCount; ++i) {
+        b.local_get(typeArguments);
+        b.i32_const(i);
+        b.array_get(translator.types.typeArrayArrayType);
+        b.local_get(b.locals[1 + i]);
+        b.call(translator.functions
+            .getFunction(translator.isTypeSubtype.reference));
+        {
+          b.local_set(boolTemp);
+          final w.Label okBlock = b.block(const [], const []);
+          b.local_get(boolTemp);
+          b.i32_const(1);
+          b.i32_eq();
+          b.br_if(okBlock);
+          b.i32_const(0);
+          b.br(resultLabel);
+          b.end();
+        }
+      }
+      b.i32_const(1);
+      b.br(resultLabel);
+    } else {
+      if (interfaceClass == translator.coreTypes.objectClass) {
+        b.drop();
+        b.i32_const(1);
+      } else if (interfaceClass == translator.coreTypes.functionClass) {
+        b.local_get(operand);
+        b.ref_test(translator.closureInfo.nonNullableType);
+      } else {
+        final ranges = translator.classIdNumbering
+            .getConcreteClassIdRanges(interfaceClass);
+        b.local_get(operand);
+        b.struct_get(translator.topInfo.struct, FieldIndex.classId);
+        b.emitClassIdRangeCheck(ranges);
+      }
+      b.br(resultLabel);
+    }
+
+    if (operandIsNullable) {
+      b.end(); // nullLabel
+      b.i32_const(encodedNullability(testedAgainstType));
+    }
+    b.end(); // resultLabel
+
+    b.return_();
+    b.end();
+  }
+}
+
+class AsCheckerCodeGenerator implements CodeGenerator {
+  final Translator translator;
+
+  final w.ValueType argumentType;
+  final w.ValueType returnType;
+
+  final InterfaceType testedAgainstType;
+  final bool operandIsNullable;
+  final bool checkArguments;
+  final int argumentCount;
+
+  AsCheckerCodeGenerator(
+      this.translator,
+      this.argumentType,
+      this.returnType,
+      this.testedAgainstType,
+      this.operandIsNullable,
+      this.checkArguments,
+      this.argumentCount);
+
+  @override
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null);
+
+    w.Label asCheckBlock = b.block();
+    b.local_get(b.locals[0]);
+    for (int i = 0; i < argumentCount; ++i) {
+      b.local_get(b.locals[1 + i]);
+    }
+    b.call(translator.types._generateIsChecker(
+        testedAgainstType, checkArguments, operandIsNullable));
+    b.br_if(asCheckBlock);
+
+    if (checkArguments) {
+      final testedAgainstClassId =
+          translator.classInfo[testedAgainstType.classNode]!.classId;
+      b.local_get(b.locals[0]);
+      b.i32_const(encodedNullability(testedAgainstType));
+      b.i32_const(testedAgainstClassId);
+      if (argumentCount == 1) {
+        b.local_get(b.locals[1]);
+        b.call(translator.functions
+            .getFunction(translator.throwInterfaceTypeAsCheckError1.reference));
+      } else if (argumentCount == 2) {
+        b.local_get(b.locals[1]);
+        b.local_get(b.locals[2]);
+        b.call(translator.functions
+            .getFunction(translator.throwInterfaceTypeAsCheckError2.reference));
+      } else {
+        for (int i = 0; i < argumentCount; ++i) {
+          b.local_get(b.locals[1 + i]);
+        }
+        b.array_new_fixed(translator.types.typeArrayArrayType, argumentCount);
+        b.call(translator.functions
+            .getFunction(translator.throwInterfaceTypeAsCheckError.reference));
+      }
+    } else {
+      b.local_get(b.locals[0]);
+      translator.constants.instantiateConstant(
+          b,
+          TypeLiteralConstant(testedAgainstType),
+          translator.types.nonNullableTypeType);
+      b.call(translator.functions
+          .getFunction(translator.throwAsCheckError.reference));
+    }
+    b.unreachable();
+
+    b.end();
+
+    b.local_get(b.locals[0]);
+    translator.convertType(b, argumentType, returnType);
+    b.return_();
+    b.end();
+  }
 }
 
 /// Builds up data structures that the Runtime Type System implementation uses.