[dart2wasm] Decouple various code from the current function that is being compiled

There's various components in the code generation that should not depend
on the current function. This is especially relevant when inlining, as
we want to emit wasm for the inlined body in the context of an unrelated
caller function. The codegen for the inlined body should therefore not
be aware or require knowing anything about the function it's being
inlined into.

Also results in net removal of code.

Change-Id: I808b6f10fa3f262ec7e0b5a4b2b6e42d60ff6582
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/378144
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index ff526b1..deeca32 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -115,8 +115,8 @@
 
     // (1) Create async state.
 
-    final asyncStateLocal = function
-        .addLocal(w.RefType(asyncSuspendStateInfo.struct, nullable: false));
+    final asyncStateLocal =
+        b.addLocal(w.RefType(asyncSuspendStateInfo.struct, nullable: false));
 
     // AsyncResumeFun _resume
     b.global_get(translator.makeFunctionRef(resumeFun));
@@ -157,7 +157,7 @@
     b.struct_get(
         asyncSuspendStateInfo.struct, FieldIndex.asyncSuspendStateCompleter);
     translator.convertType(
-        function,
+        b,
         asyncSuspendStateInfo.struct.fields[5].type.unpacked,
         completerFutureGetterType.inputs[0]);
     call(translator.completerFuture.getterReference);
@@ -171,17 +171,19 @@
     // Set the current Wasm function for the code generator to the inner
     // function of the `async`, which is to contain the body.
     function = resumeFun;
+    b = resumeFun.body;
+    functionType = resumeFun.type;
 
     // Set up locals for contexts and `this`.
     thisLocal = null;
     Context? localContext = context;
     while (localContext != null) {
       if (!localContext.isEmpty) {
-        localContext.currentLocal = function
-            .addLocal(w.RefType.def(localContext.struct, nullable: true));
+        localContext.currentLocal =
+            b.addLocal(w.RefType.def(localContext.struct, nullable: true));
         if (localContext.containsThis) {
           assert(thisLocal == null);
-          thisLocal = function.addLocal(localContext
+          thisLocal = b.addLocal(localContext
               .struct.fields[localContext.thisFieldIndex].type.unpacked
               .withNullability(false));
           translator.globals.instantiateDummyValue(b, thisLocal!.type);
@@ -375,7 +377,7 @@
     setVariable(awaitValueVar, () {
       b.local_get(_awaitValueLocal);
       translator.convertType(
-          function, _awaitValueLocal.type, translateType(awaitValueVar.type));
+          b, _awaitValueLocal.type, translateType(awaitValueVar.type));
     });
   }
 }
diff --git a/pkg/dart2wasm/lib/closures.dart b/pkg/dart2wasm/lib/closures.dart
index c565f2c..d91107b 100644
--- a/pkg/dart2wasm/lib/closures.dart
+++ b/pkg/dart2wasm/lib/closures.dart
@@ -529,7 +529,7 @@
 
     // Cast context reference to actual context type.
     w.RefType contextType = w.RefType.def(contextStruct, nullable: false);
-    w.Local contextLocal = trampoline.addLocal(contextType);
+    w.Local contextLocal = b.addLocal(contextType);
     b.local_get(trampoline.locals[0]);
     b.ref_cast(contextType);
     b.local_tee(contextLocal);
@@ -579,7 +579,7 @@
     final w.RefType instantiationContextType =
         w.RefType.def(instantiationContextStruct, nullable: false);
     final w.Local instantiationContextLocal =
-        function.addLocal(instantiationContextType);
+        b.addLocal(instantiationContextType);
     b.local_get(instantiatedClosureLocal);
     b.struct_get(closureBaseStruct, FieldIndex.closureContext);
     b.ref_cast(instantiationContextType);
@@ -590,7 +590,7 @@
         instantiationContextStruct, FieldIndex.instantiationContextInner);
 
     // Push types
-    translator.makeArray(function, translator.typeArrayType, typeCount,
+    translator.makeArray(b, translator.typeArrayType, typeCount,
         (elementType, elementIdx) {
       b.local_get(instantiationContextLocal);
       b.struct_get(instantiationContextStruct,
@@ -641,8 +641,8 @@
     ib.end();
 
     final instantiationFunction = m.functions.define(functionType, name);
-    w.Local preciseClosure = instantiationFunction.addLocal(genericClosureType);
     final b = instantiationFunction.body;
+    w.Local preciseClosure = b.addLocal(genericClosureType);
 
     // Parameters to the instantiation function
     final w.Local closureParam = instantiationFunction.locals[0];
@@ -703,8 +703,8 @@
     final thisContext = function.locals[0];
     final otherContext = function.locals[1];
 
-    final thisContextLocal = function.addLocal(contextRefType);
-    final otherContextLocal = function.addLocal(contextRefType);
+    final thisContextLocal = b.addLocal(contextRefType);
+    final otherContextLocal = b.addLocal(contextRefType);
 
     // Call site (`_Closure._equals`) checks that closures are instantiations
     // of the same function, so we can assume they have the right instantiation
@@ -751,7 +751,7 @@
     final contextRefType = w.RefType.def(contextStructType, nullable: false);
 
     final thisContext = function.locals[0];
-    final thisContextLocal = function.addLocal(contextRefType);
+    final thisContextLocal = b.addLocal(contextRefType);
 
     b.local_get(thisContext);
     b.ref_cast(contextRefType);
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 51e6f0b..6d96df2 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -40,7 +40,8 @@
     with ExpressionVisitor1DefaultMixin<w.ValueType, w.ValueType>
     implements InitializerVisitor<void>, StatementVisitor<void> {
   final Translator translator;
-  w.FunctionBuilder function;
+  w.FunctionType functionType;
+  w.InstructionsBuilder b;
   final Reference reference;
   late final List<w.Local> paramLocals;
   final w.Label? returnLabel;
@@ -86,9 +87,8 @@
   /// code for an inlined function by specifying the locals containing the
   /// parameters (instead of the function inputs) and the label to jump to on
   /// return (instead of emitting a `return` instruction).
-  CodeGenerator(this.translator, this.function, this.reference,
-      {List<w.Local>? paramLocals, this.returnLabel}) {
-    this.paramLocals = paramLocals ?? function.locals.toList();
+  CodeGenerator(this.translator, this.functionType, this.b, this.reference,
+      {required this.paramLocals, this.returnLabel}) {
     intrinsifier = Intrinsifier(this);
     typeContext = StaticTypeContext(member, translator.typeEnvironment);
   }
@@ -112,17 +112,16 @@
     } else if (!isTypeChecker && isAsync) {
       return AsyncCodeGenerator(translator, function, reference);
     } else {
-      return CodeGenerator(translator, function, reference);
+      return CodeGenerator(translator, function.type, function.body, reference,
+          paramLocals: function.locals.toList());
     }
   }
 
   w.ModuleBuilder get m => translator.m;
-  w.InstructionsBuilder get b => function.body;
 
   Member get member => reference.asMember;
 
-  List<w.ValueType> get outputs =>
-      returnLabel?.targetTypes ?? function.type.outputs;
+  List<w.ValueType> get outputs => functionType.outputs;
 
   w.ValueType get returnType => translator.outputOrVoid(outputs);
 
@@ -135,7 +134,7 @@
   w.ValueType translateType(DartType type) => translator.translateType(type);
 
   w.Local addLocal(w.ValueType type) {
-    return function.addLocal(type);
+    return b.addLocal(type);
   }
 
   DartType dartTypeOf(Expression exp) {
@@ -245,7 +244,7 @@
     }
 
     if (intrinsifier.generateMemberIntrinsic(
-        reference, function, paramLocals, returnLabel)) {
+        reference, functionType, paramLocals, returnLabel)) {
       b.end();
       return;
     }
@@ -258,7 +257,6 @@
         b.ref_null(w.HeapType.none);
       }
       translator.constants.instantiateConstant(
-          function,
           b,
           SymbolConstant(member.name.text, null),
           translator.classInfo[translator.symbolClass]!.nonNullableType);
@@ -335,7 +333,7 @@
       b.global_set(flag);
     }
     b.global_get(global);
-    translator.convertType(function, global.type.type, outputs.single);
+    translator.convertType(b, global.type.type, outputs.single);
     b.end();
   }
 
@@ -349,20 +347,20 @@
       w.Local thisLocal = paramLocals[0];
       w.RefType structType = w.RefType.def(struct, nullable: false);
       b.local_get(thisLocal);
-      translator.convertType(function, thisLocal.type, structType);
+      translator.convertType(b, thisLocal.type, structType);
     }
 
     if (reference.isImplicitGetter) {
       // Implicit getter
       getThis();
       b.struct_get(struct, fieldIndex);
-      translator.convertType(function, fieldType, returnType);
+      translator.convertType(b, fieldType, returnType);
     } else {
       // Implicit setter
       w.Local valueLocal = paramLocals[1];
       getThis();
       b.local_get(valueLocal);
-      translator.convertType(function, valueLocal.type, fieldType);
+      translator.convertType(b, valueLocal.type, fieldType);
       b.struct_set(struct, fieldIndex);
     }
     b.end();
@@ -407,7 +405,7 @@
         // have to handle sentinel before we can downcast the value.
         b.local_get(local);
         translator.constants.instantiateConstant(
-            function, b, ParameterInfo.defaultValueSentinel, local.type);
+            b, ParameterInfo.defaultValueSentinel, local.type);
         b.ref_eq();
         b.if_();
         wrap(variable.initializer!, local.type);
@@ -424,7 +422,7 @@
         if (!local.type.isSubtypeOf(incomingArgumentType)) {
           final newLocal = addLocal(incomingArgumentType);
           b.local_get(local);
-          translator.convertType(function, local.type, newLocal.type);
+          translator.convertType(b, local.type, newLocal.type);
           b.local_set(newLocal);
           local = newLocal;
         }
@@ -438,7 +436,7 @@
           if (!operand.type.isSubtypeOf(boxedType)) {
             final boxedOperand = addLocal(boxedType);
             b.local_get(operand);
-            translator.convertType(function, operand.type, boxedOperand.type);
+            translator.convertType(b, operand.type, boxedOperand.type);
             b.local_set(boxedOperand);
             operand = boxedOperand;
           }
@@ -459,7 +457,7 @@
         if (!variableType.isSubtypeOf(local.type)) {
           w.Local newLocal = addLocal(variableType);
           b.local_get(local);
-          translator.convertType(function, local.type, newLocal.type);
+          translator.convertType(b, local.type, newLocal.type);
           b.local_set(newLocal);
           local = newLocal;
         }
@@ -490,7 +488,7 @@
               parameterType.classNode == translator.wasmExternRefClass)) {
         w.Local newLocal = addLocal(translateType(parameterType));
         b.local_get(local);
-        translator.convertType(function, local.type, newLocal.type);
+        translator.convertType(b, local.type, newLocal.type);
         b.local_set(newLocal);
         locals[parameter] = newLocal;
       }
@@ -790,7 +788,7 @@
   }
 
   /// Generate code for the body of a lambda.
-  w.BaseFunction generateLambda(Lambda lambda, Closures closures) {
+  void generateLambda(Lambda lambda, Closures closures) {
     // Initialize closure information from enclosing member.
     this.closures = closures;
 
@@ -803,8 +801,6 @@
     visitStatement(lambda.functionNode.body!);
     _implicitReturn();
     b.end();
-
-    return function;
   }
 
   /// Initialize locals containing `this` in constructors and instance members.
@@ -821,7 +817,7 @@
       if (translator.needsConversion(thisLocal!.type, preciseThisType)) {
         preciseThisLocal = addLocal(preciseThisType);
         b.local_get(thisLocal!);
-        translator.convertType(function, thisLocal!.type, preciseThisType);
+        translator.convertType(b, thisLocal!.type, preciseThisType);
         b.local_set(preciseThisLocal!);
       } else {
         preciseThisLocal = thisLocal!;
@@ -929,7 +925,7 @@
       if (capture != null) {
         b.local_get(capture.context.currentLocal);
         b.local_get(local);
-        translator.convertType(function, local.type, capture.type);
+        translator.convertType(b, local.type, capture.type);
         b.struct_set(capture.context.struct, capture.fieldIndex);
       }
     });
@@ -938,7 +934,7 @@
       if (capture != null) {
         b.local_get(capture.context.currentLocal);
         b.local_get(local);
-        translator.convertType(function, local.type, capture.type);
+        translator.convertType(b, local.type, capture.type);
         b.struct_set(capture.context.struct, capture.fieldIndex);
       }
     });
@@ -967,7 +963,7 @@
     final oldFileOffset = setSourceMapFileOffset(node.fileOffset);
     try {
       w.ValueType resultType = node.accept1(this, expectedType);
-      translator.convertType(function, resultType, expectedType);
+      translator.convertType(b, resultType, expectedType);
       return expectedType;
     } catch (_) {
       _printLocation(node);
@@ -1019,7 +1015,7 @@
       }
       w.Label block = b.block(const [], targetFunctionType.outputs);
       b.comment("Inlined ${target.asMember}");
-      CodeGenerator(translator, function, target,
+      CodeGenerator(translator, targetFunctionType, b, target,
               paramLocals: inlinedLocals, returnLabel: block)
           .generate();
       return targetFunctionType.outputs;
@@ -1213,7 +1209,6 @@
           translator.classInfo[stringClass]!.nullableType;
       if (location != null) {
         translator.constants.instantiateConstant(
-          function,
           b,
           StringConstant(location.file.toString()),
           stringRefType,
@@ -1225,7 +1220,6 @@
         final String conditionString = sourceString.substring(
             node.conditionStartOffset, node.conditionEndOffset);
         translator.constants.instantiateConstant(
-          function,
           b,
           StringConstant(conditionString),
           stringRefType,
@@ -1300,7 +1294,7 @@
           b.local_get(thrownException);
           // Type test passed, downcast the exception to the expected type.
           translator.convertType(
-            function,
+            b,
             thrownException.type,
             translator.translateType(exceptionDeclaration.type),
           );
@@ -1452,7 +1446,7 @@
       } else {
         if (returnValueLocal != null) {
           b.local_get(returnValueLocal!);
-          translator.convertType(function, returnValueLocal!.type, returnType);
+          translator.convertType(b, returnValueLocal!.type, returnType);
         }
         _returnFromFunction();
       }
@@ -1641,7 +1635,7 @@
     if (expression != null) {
       wrap(expression, returnType);
     } else {
-      translator.convertType(function, voidMarker, returnType);
+      translator.convertType(b, voidMarker, returnType);
     }
 
     // If we are wrapped in a [TryFinally] node then we have to run finalizers
@@ -1720,9 +1714,7 @@
               : doneLabel;
       b.local_get(switchValueNullableLocal!);
       b.br_on_null(nullLabel);
-      translator.convertType(
-          function,
-          switchInfo.nullableType.withNullability(false),
+      translator.convertType(b, switchInfo.nullableType.withNullability(false),
           switchInfo.nonNullableType);
       b.local_set(switchValueNonNullableLocal);
     }
@@ -1852,7 +1844,7 @@
     // A user of `this` may have more precise type information, in which case
     // we downcast it here.
     b.local_get(thisLocal!);
-    translator.convertType(function, thisType, expectedType);
+    translator.convertType(b, thisType, expectedType);
     return expectedType;
   }
 
@@ -2020,12 +2012,11 @@
 
     // Evaluate receiver
     wrap(receiver, translator.topInfo.nullableType);
-    final nullableReceiverLocal =
-        function.addLocal(translator.topInfo.nullableType);
+    final nullableReceiverLocal = addLocal(translator.topInfo.nullableType);
     b.local_set(nullableReceiverLocal);
 
     // Evaluate type arguments.
-    final typeArgsLocal = function.addLocal(
+    final typeArgsLocal = addLocal(
         makeArray(translator.typeArrayType, typeArguments.length,
             (elementType, elementIdx) {
       translator.types.makeType(this, typeArguments[elementIdx]);
@@ -2033,7 +2024,7 @@
     b.local_set(typeArgsLocal);
 
     // Evaluate positional arguments
-    final positionalArgsLocal = function.addLocal(makeArray(
+    final positionalArgsLocal = addLocal(makeArray(
         translator.nullableObjectArrayType, positionalArguments.length,
         (elementType, elementIdx) {
       wrap(positionalArguments[elementIdx], elementType);
@@ -2047,14 +2038,14 @@
     final List<MapEntry<String, w.Local>> namedArgumentLocals = [];
     for (final namedArgument in namedArguments) {
       wrap(namedArgument.value, translator.topInfo.nullableType);
-      final argumentLocal = function.addLocal(translator.topInfo.nullableType);
+      final argumentLocal = addLocal(translator.topInfo.nullableType);
       b.local_set(argumentLocal);
       namedArgumentLocals.add(MapEntry(namedArgument.name, argumentLocal));
     }
     namedArgumentLocals.sort((e1, e2) => e1.key.compareTo(e2.key));
 
     // Create named argument array
-    final namedArgsLocal = function.addLocal(
+    final namedArgsLocal = addLocal(
         makeArray(translator.nullableObjectArrayType, namedArguments.length * 2,
             (elementType, elementIdx) {
       if (elementIdx % 2 == 0) {
@@ -2062,7 +2053,7 @@
         final w.ValueType symbolValueType =
             translator.classInfo[translator.symbolClass]!.nonNullableType;
         translator.constants.instantiateConstant(
-            function, b, SymbolConstant(name, null), symbolValueType);
+            b, SymbolConstant(name, null), symbolValueType);
       } else {
         final local = namedArgumentLocals[elementIdx ~/ 2].value;
         b.local_get(local);
@@ -2077,8 +2068,8 @@
     // invocation of `noSuchMethod` (done in [_callNoSuchMethod]), but we don't
     // have a `Null` class in dart2wasm so we throw directly.
     b.local_get(nullableReceiverLocal);
-    createInvocationObject(translator, function, forwarder.memberName,
-        typeArgsLocal, positionalArgsLocal, namedArgsLocal);
+    createInvocationObject(translator, b, forwarder.memberName, typeArgsLocal,
+        positionalArgsLocal, namedArgsLocal);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
     b.unreachable();
@@ -2317,7 +2308,7 @@
   @override
   w.ValueType visitStaticTearOff(StaticTearOff node, w.ValueType expectedType) {
     translator.constants.instantiateConstant(
-        function, b, StaticTearOffConstant(node.target), expectedType);
+        b, StaticTearOffConstant(node.target), expectedType);
     return expectedType;
   }
 
@@ -2434,8 +2425,7 @@
 
     // Evaluate receiver
     wrap(receiver, translator.topInfo.nullableType);
-    final nullableReceiverLocal =
-        function.addLocal(translator.topInfo.nullableType);
+    final nullableReceiverLocal = addLocal(translator.topInfo.nullableType);
     b.local_set(nullableReceiverLocal);
 
     final nullBlock = b.block([], [translator.topInfo.nonNullableType]);
@@ -2445,7 +2435,7 @@
     // invocation of `noSuchMethod` (done in [_callNoSuchMethod]), but we don't
     // have a `Null` class in dart2wasm so we throw directly.
     b.local_get(nullableReceiverLocal);
-    createGetterInvocationObject(translator, function, forwarder.memberName);
+    createGetterInvocationObject(translator, b, forwarder.memberName);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
     b.unreachable();
@@ -2466,14 +2456,12 @@
 
     // Evaluate receiver
     wrap(receiver, translator.topInfo.nullableType);
-    final nullableReceiverLocal =
-        function.addLocal(translator.topInfo.nullableType);
+    final nullableReceiverLocal = addLocal(translator.topInfo.nullableType);
     b.local_set(nullableReceiverLocal);
 
     // Evaluate positional arg
     wrap(value, translator.topInfo.nullableType);
-    final positionalArgLocal =
-        function.addLocal(translator.topInfo.nullableType);
+    final positionalArgLocal = addLocal(translator.topInfo.nullableType);
     b.local_set(positionalArgLocal);
 
     final nullBlock = b.block([], [translator.topInfo.nonNullableType]);
@@ -2484,7 +2472,7 @@
     // have a `Null` class in dart2wasm so we throw directly.
     b.local_get(nullableReceiverLocal);
     createSetterInvocationObject(
-        translator, function, forwarder.memberName, positionalArgLocal);
+        translator, b, forwarder.memberName, positionalArgLocal);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
     b.unreachable();
@@ -2534,7 +2522,7 @@
         wrap(node.receiver, translator.topInfo.nullableType);
         b.br_on_null(nullLabel);
         translator.convertType(
-            function, translator.topInfo.nullableType, signature.inputs[0]);
+            b, translator.topInfo.nullableType, signature.inputs[0]);
       }, (_, __) {});
       b.br(doneLabel);
       b.end(); // nullLabel
@@ -2888,7 +2876,7 @@
     for (int i = positional.length; i < paramInfo.positional.length; i++) {
       final w.ValueType type = signature.inputs[signatureOffset + i];
       translator.constants
-          .instantiateConstant(function, b, paramInfo.positional[i]!, type);
+          .instantiateConstant(b, paramInfo.positional[i]!, type);
     }
     // Named arguments
     final Map<String, w.Local> namedLocals = {};
@@ -2908,7 +2896,7 @@
         b.local_get(namedLocal);
       } else {
         translator.constants
-            .instantiateConstant(function, b, paramInfo.named[name]!, type);
+            .instantiateConstant(b, paramInfo.named[name]!, type);
       }
     }
   }
@@ -2994,43 +2982,41 @@
   @override
   w.ValueType visitConstantExpression(
       ConstantExpression node, w.ValueType expectedType) {
-    translator.constants
-        .instantiateConstant(function, b, node.constant, expectedType);
+    translator.constants.instantiateConstant(b, node.constant, expectedType);
     return expectedType;
   }
 
   @override
   w.ValueType visitNullLiteral(NullLiteral node, w.ValueType expectedType) {
-    translator.constants
-        .instantiateConstant(function, b, NullConstant(), expectedType);
+    translator.constants.instantiateConstant(b, NullConstant(), expectedType);
     return expectedType;
   }
 
   @override
   w.ValueType visitStringLiteral(StringLiteral node, w.ValueType expectedType) {
-    translator.constants.instantiateConstant(
-        function, b, StringConstant(node.value), expectedType);
+    translator.constants
+        .instantiateConstant(b, StringConstant(node.value), expectedType);
     return expectedType;
   }
 
   @override
   w.ValueType visitBoolLiteral(BoolLiteral node, w.ValueType expectedType) {
-    translator.constants.instantiateConstant(
-        function, b, BoolConstant(node.value), expectedType);
+    translator.constants
+        .instantiateConstant(b, BoolConstant(node.value), expectedType);
     return expectedType;
   }
 
   @override
   w.ValueType visitIntLiteral(IntLiteral node, w.ValueType expectedType) {
-    translator.constants.instantiateConstant(
-        function, b, IntConstant(node.value), expectedType);
+    translator.constants
+        .instantiateConstant(b, IntConstant(node.value), expectedType);
     return expectedType;
   }
 
   @override
   w.ValueType visitDoubleLiteral(DoubleLiteral node, w.ValueType expectedType) {
-    translator.constants.instantiateConstant(
-        function, b, DoubleConstant(node.value), expectedType);
+    translator.constants
+        .instantiateConstant(b, DoubleConstant(node.value), expectedType);
     return expectedType;
   }
 
@@ -3075,7 +3061,7 @@
 
   w.ValueType makeArray(w.ArrayType arrayType, int length,
       void Function(w.ValueType, int) generateItem) {
-    return translator.makeArray(function, arrayType, length, generateItem);
+    return translator.makeArray(b, arrayType, length, generateItem);
   }
 
   @override
@@ -3224,7 +3210,7 @@
       b.struct_get(info.struct, fieldIndex);
       resultType = info.struct.fields[fieldIndex].type.unpacked;
     }
-    translator.convertType(function, resultType, types.nonNullableTypeType);
+    translator.convertType(b, resultType, types.nonNullableTypeType);
     return types.nonNullableTypeType;
   }
 
@@ -3425,8 +3411,8 @@
   /// This function will be called by a setter forwarder in a dynamic set to
   /// type check the setter argument before calling the actual setter.
   void _generateFieldSetterTypeCheckerMethod() {
-    final receiverLocal = function.locals[0];
-    final positionalArgLocal = function.locals[1];
+    final receiverLocal = paramLocals[0];
+    final positionalArgLocal = paramLocals[1];
 
     _initializeThis(member.reference);
 
@@ -3454,11 +3440,10 @@
     if (member_ is Field) {
       int fieldIndex = translator.fieldIndex[member_]!;
       b.local_get(receiverLocal);
-      translator.convertType(
-          function, receiverLocal.type, info.nonNullableType);
+      translator.convertType(b, receiverLocal.type, info.nonNullableType);
       b.local_get(argLocal);
-      translator.convertType(function, argLocal.type,
-          info.struct.fields[fieldIndex].type.unpacked);
+      translator.convertType(
+          b, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
       b.struct_set(info.struct, fieldIndex);
     } else {
       final setterProcedure = member_ as Procedure;
@@ -3467,9 +3452,9 @@
       final setterWasmInputs = setterProcedureWasmType.inputs;
       assert(setterWasmInputs.length == 2);
       b.local_get(receiverLocal);
-      translator.convertType(function, receiverLocal.type, setterWasmInputs[0]);
+      translator.convertType(b, receiverLocal.type, setterWasmInputs[0]);
       b.local_get(argLocal);
-      translator.convertType(function, argLocal.type, setterWasmInputs[1]);
+      translator.convertType(b, argLocal.type, setterWasmInputs[1]);
       call(setterProcedure.reference);
     }
 
@@ -3482,10 +3467,10 @@
   /// This function will be called by an invocation forwarder in a dynamic
   /// invocation to type check parameters before calling the actual method.
   void _generateProcedureTypeCheckerMethod() {
-    final receiverLocal = function.locals[0];
-    final typeArgsLocal = function.locals[1];
-    final positionalArgsLocal = function.locals[2];
-    final namedArgsLocal = function.locals[3];
+    final receiverLocal = paramLocals[0];
+    final typeArgsLocal = paramLocals[1];
+    final positionalArgsLocal = paramLocals[2];
+    final namedArgsLocal = paramLocals[3];
 
     _initializeThis(member.reference);
 
@@ -3605,7 +3590,7 @@
     final List<w.ValueType> memberWasmInputs = memberWasmFunctionType.inputs;
 
     b.local_get(receiverLocal);
-    translator.convertType(function, receiverLocal.type, memberWasmInputs[0]);
+    translator.convertType(b, receiverLocal.type, memberWasmInputs[0]);
 
     for (final typeParam in memberTypeParams) {
       b.local_get(typeLocals[typeParam]!);
@@ -3618,8 +3603,8 @@
       b.local_get(listLocal);
       b.i32_const(listIdx);
       b.array_get(translator.nullableObjectArrayType);
-      translator.convertType(function, translator.topInfo.nullableType,
-          memberWasmInputs[wasmInputIdx]);
+      translator.convertType(
+          b, translator.topInfo.nullableType, memberWasmInputs[wasmInputIdx]);
     }
 
     for (int positionalParamIdx = 0;
@@ -3639,7 +3624,7 @@
     call(member.reference);
 
     translator.convertType(
-        function,
+        b,
         translator.outputOrVoid(memberWasmFunctionType.outputs),
         translator.topInfo.nullableType);
 
@@ -3697,8 +3682,8 @@
     DartType bound,
   ) {
     b.local_get(typeLocal);
-    final boundLocal = function
-        .addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
+    final boundLocal =
+        b.addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
     types.makeType(this, bound);
     b.local_tee(boundLocal);
     call(translator.isTypeSubtype.reference);
@@ -3743,7 +3728,7 @@
     final printFunction =
         translator.functions.getFunction(translator.printToConsole.reference);
     translator.constants.instantiateConstant(
-        function, b, StringConstant(s), printFunction.type.inputs[0]);
+        b, StringConstant(s), printFunction.type.inputs[0]);
     b.call(printFunction);
   }
 
@@ -3877,11 +3862,11 @@
       compare = (switchExprLocal, pushCaseExpr) {
         final caseExprType = pushCaseExpr();
         translator.convertType(
-            codeGen.function, caseExprType, equalsMemberSignature.inputs[0]);
+            codeGen.b, caseExprType, equalsMemberSignature.inputs[0]);
 
         codeGen.b.local_get(switchExprLocal);
-        translator.convertType(codeGen.function, switchExprLocal.type,
-            equalsMemberSignature.inputs[1]);
+        translator.convertType(
+            codeGen.b, switchExprLocal.type, equalsMemberSignature.inputs[1]);
 
         codeGen.call(equalsMember.reference);
       };
@@ -4017,7 +4002,7 @@
       return;
     }
 
-    w.Local classId = addLocal(w.NumType.i32, isParameter: false);
+    w.Local classId = addLocal(w.NumType.i32);
     local_set(classId);
 
     final done = block([], outputs);
@@ -4092,7 +4077,7 @@
       return;
     }
 
-    w.Local classId = addLocal(w.NumType.i32, isParameter: false);
+    w.Local classId = addLocal(w.NumType.i32);
     local_set(classId);
     final done = block([], outputs);
     for (final (:range, :value) in ranges) {
@@ -4242,8 +4227,7 @@
   w.Local cloneFunctionLevelContext(
       Closures closures, Context context, FunctionNode functionNode) {
     final w.Local srcContext = context.currentLocal;
-    final w.Local destContext =
-        addLocal(context.currentLocal.type, isParameter: false);
+    final w.Local destContext = addLocal(context.currentLocal.type);
 
     struct_new_default(context.struct);
     local_set(destContext);
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index d1525f6..05e3a59 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -28,8 +28,7 @@
   bool get isLazy => function != null;
 }
 
-typedef ConstantCodeGenerator = void Function(
-    w.FunctionBuilder?, w.InstructionsBuilder);
+typedef ConstantCodeGenerator = void Function(w.InstructionsBuilder);
 
 /// Handles the creation of Dart constants.
 ///
@@ -130,10 +129,10 @@
   }
 
   /// Emit code to push a constant onto the stack.
-  void instantiateConstant(w.BaseFunction? function, w.InstructionsBuilder b,
-      Constant constant, w.ValueType expectedType) {
+  void instantiateConstant(
+      w.InstructionsBuilder b, Constant constant, w.ValueType expectedType) {
     if (expectedType == translator.voidMarker) return;
-    ConstantInstantiator(this, function, b, expectedType).instantiate(constant);
+    ConstantInstantiator(this, b, expectedType).instantiate(constant);
   }
 
   InstanceConstant _lowerTypeConstant(DartType type) {
@@ -293,12 +292,10 @@
 class ConstantInstantiator extends ConstantVisitor<w.ValueType>
     with ConstantVisitorDefaultMixin<w.ValueType> {
   final Constants constants;
-  final w.BaseFunction? function;
   final w.InstructionsBuilder b;
   final w.ValueType expectedType;
 
-  ConstantInstantiator(
-      this.constants, this.function, this.b, this.expectedType);
+  ConstantInstantiator(this.constants, this.b, this.expectedType);
 
   Translator get translator => constants.translator;
   w.ModuleBuilder get m => translator.m;
@@ -453,9 +450,9 @@
       global.initializer.end();
       w.FunctionType ftype = m.types.defineFunction(const [], [type]);
       final function = m.functions.define(ftype, "$constant");
-      generator(function, function.body);
-      w.Local temp = function.addLocal(type);
       final b2 = function.body;
+      generator(b2);
+      w.Local temp = b2.addLocal(type);
       b2.local_tee(temp);
       b2.global_set(global);
       b2.local_get(temp);
@@ -467,7 +464,7 @@
       assert(!constants.currentlyCreating);
       constants.currentlyCreating = true;
       final global = m.globals.define(w.GlobalType(type, mutable: false));
-      generator(null, global.initializer);
+      generator(global.initializer);
       global.initializer.end();
       constants.currentlyCreating = false;
 
@@ -481,7 +478,7 @@
   @override
   ConstantInfo? visitBoolConstant(BoolConstant constant) {
     ClassInfo info = translator.classInfo[translator.boxedBoolClass]!;
-    return createConstant(constant, info.nonNullableType, (function, b) {
+    return createConstant(constant, info.nonNullableType, (b) {
       b.i32_const(info.classId);
       b.i32_const(constant.value ? 1 : 0);
       b.struct_new(info.struct);
@@ -491,7 +488,7 @@
   @override
   ConstantInfo? visitIntConstant(IntConstant constant) {
     ClassInfo info = translator.classInfo[translator.boxedIntClass]!;
-    return createConstant(constant, info.nonNullableType, (function, b) {
+    return createConstant(constant, info.nonNullableType, (b) {
       b.i32_const(info.classId);
       b.i64_const(constant.value);
       b.struct_new(info.struct);
@@ -501,7 +498,7 @@
   @override
   ConstantInfo? visitDoubleConstant(DoubleConstant constant) {
     ClassInfo info = translator.classInfo[translator.boxedDoubleClass]!;
-    return createConstant(constant, info.nonNullableType, (function, b) {
+    return createConstant(constant, info.nonNullableType, (b) {
       b.i32_const(info.classId);
       b.f64_const(constant.value);
       b.struct_new(info.struct);
@@ -512,7 +509,7 @@
   ConstantInfo? visitStringConstant(StringConstant constant) {
     if (translator.options.jsCompatibility) {
       ClassInfo info = translator.classInfo[translator.jsStringClass]!;
-      return createConstant(constant, info.nonNullableType, (function, b) {
+      return createConstant(constant, info.nonNullableType, (b) {
         b.i32_const(info.classId);
         b.i32_const(initialIdentityHash);
         b.global_get(translator.getInternalizedStringGlobal(constant.value));
@@ -526,7 +523,7 @@
     translator.functions.recordClassAllocation(info.classId);
     w.RefType type = info.nonNullableType;
     bool lazy = constant.value.length > maxArrayNewFixedLength;
-    return createConstant(constant, type, lazy: lazy, (function, b) {
+    return createConstant(constant, type, lazy: lazy, (b) {
       w.ArrayType arrayType =
           (info.struct.fields[FieldIndex.stringArray].type as w.RefType)
               .heapType as w.ArrayType;
@@ -605,13 +602,13 @@
       args = supertype.typeArguments;
     }
 
-    return createConstant(constant, type, lazy: lazy, (function, b) {
+    return createConstant(constant, type, lazy: lazy, (b) {
       b.i32_const(info.classId);
       b.i32_const(initialIdentityHash);
       for (int i = baseFieldCount; i < fieldCount; i++) {
         Constant subConstant = subConstants[i]!;
         constants.instantiateConstant(
-            function, b, subConstant, info.struct.fields[i].type.unpacked);
+            b, subConstant, info.struct.fields[i].type.unpacked);
       }
       b.struct_new(info.struct);
     });
@@ -631,7 +628,7 @@
     }
 
     return createConstant(constant, w.RefType.def(arrayType, nullable: false),
-        lazy: lazy, (function, b) {
+        lazy: lazy, (b) {
       if (tooLargeForArrayNewFixed) {
         // We will initialize the array with one of the elements (using
         // `array.new`) and update the fields.
@@ -653,8 +650,8 @@
         }
 
         w.Local arrayLocal =
-            function!.addLocal(w.RefType.def(arrayType, nullable: false));
-        constants.instantiateConstant(function, b, initialElement, elementType);
+            b.addLocal(w.RefType.def(arrayType, nullable: false));
+        constants.instantiateConstant(b, initialElement, elementType);
         b.i32_const(elements.length);
         b.array_new(arrayType);
         b.local_set(arrayLocal);
@@ -674,7 +671,7 @@
 
           b.local_get(arrayLocal);
           b.i32_const(startInclusive);
-          constants.instantiateConstant(function, b, value, elementType);
+          constants.instantiateConstant(b, value, elementType);
           if (count > 1) {
             b.i32_const(count);
             b.array_fill(arrayType);
@@ -685,7 +682,7 @@
         b.local_get(arrayLocal);
       } else {
         for (Constant element in elements) {
-          constants.instantiateConstant(function, b, element, elementType);
+          constants.instantiateConstant(b, element, elementType);
         }
         b.array_new_fixed(arrayType, elements.length);
       }
@@ -705,35 +702,33 @@
     ClassInfo info = translator.classInfo[translator.immutableListClass]!;
     translator.functions.recordClassAllocation(info.classId);
     w.RefType type = info.nonNullableType;
-    return createConstant(constant, type, lazy: lazy, (function, b) {
+    return createConstant(constant, type, lazy: lazy, (b) {
       w.ArrayType arrayType = translator.listArrayType;
       w.ValueType elementType = arrayType.elementType.type.unpacked;
       int length = constant.entries.length;
       b.i32_const(info.classId);
       b.i32_const(initialIdentityHash);
       constants.instantiateConstant(
-          function, b, typeArgConstant, constants.typeInfo.nullableType);
+          b, typeArgConstant, constants.typeInfo.nullableType);
       b.i64_const(length);
       if (lazy) {
         // Allocate array and set each entry to the corresponding sub-constant.
         w.Local arrayLocal =
-            function!.addLocal(w.RefType.def(arrayType, nullable: false));
+            b.addLocal(w.RefType.def(arrayType, nullable: false));
         b.i32_const(length);
         b.array_new_default(arrayType);
         b.local_set(arrayLocal);
         for (int i = 0; i < length; i++) {
           b.local_get(arrayLocal);
           b.i32_const(i);
-          constants.instantiateConstant(
-              function, b, constant.entries[i], elementType);
+          constants.instantiateConstant(b, constant.entries[i], elementType);
           b.array_set(arrayType);
         }
         b.local_get(arrayLocal);
       } else {
         // Push all sub-constants on the stack and initialize array from them.
         for (int i = 0; i < length; i++) {
-          constants.instantiateConstant(
-              function, b, constant.entries[i], elementType);
+          constants.instantiateConstant(b, constant.entries[i], elementType);
         }
         b.array_new_fixed(arrayType, length);
       }
@@ -822,7 +817,7 @@
     ClosureImplementation closure = translator.getTearOffClosure(member);
     w.StructType struct = closure.representation.closureStruct;
     w.RefType type = w.RefType.def(struct, nullable: false);
-    return createConstant(constant, type, (function, b) {
+    return createConstant(constant, type, (b) {
       ClassInfo info = translator.closureInfo;
       translator.functions.recordClassAllocation(info.classId);
 
@@ -831,7 +826,7 @@
       b.global_get(translator.globals.dummyStructGlobal); // Dummy context
       b.global_get(closure.vtable);
       constants.instantiateConstant(
-          function, b, functionTypeConstant, types.nonNullableTypeType);
+          b, functionTypeConstant, types.nonNullableTypeType);
       b.struct_new(struct);
     });
   }
@@ -880,8 +875,7 @@
 
       b.local_get(closureLocal);
       final InstanceConstant typeArgs = constants.makeTypeArray(constant.types);
-      constants.instantiateConstant(
-          function, b, typeArgs, typeArgsListLocal.type);
+      constants.instantiateConstant(b, typeArgs, typeArgsListLocal.type);
       b.local_get(posArgsListLocal);
       b.local_get(namedArgsListLocal);
       b.call(tearOffClosure.dynamicCallEntry);
@@ -895,7 +889,7 @@
     // a constant while creating another one.
     final w.BaseFunction dynamicCallEntry = makeDynamicCallEntry();
 
-    return createConstant(constant, type, (function, b) {
+    return createConstant(constant, type, (b) {
       ClassInfo info = translator.closureInfo;
       translator.functions.recordClassAllocation(info.classId);
 
@@ -964,7 +958,7 @@
 
       makeVtable();
       constants.instantiateConstant(
-          function, b, functionTypeConstant, this.types.nonNullableTypeType);
+          b, functionTypeConstant, this.types.nonNullableTypeType);
       b.struct_new(struct);
     });
   }
@@ -982,11 +976,10 @@
         .classInfo[translator.coreTypes.stringClass]!.repr.nonNullableType;
     StringConstant nameConstant = StringConstant(constant.name);
     bool lazy = ensureConstant(nameConstant)?.isLazy ?? false;
-    return createConstant(constant, info.nonNullableType, lazy: lazy,
-        (function, b) {
+    return createConstant(constant, info.nonNullableType, lazy: lazy, (b) {
       b.i32_const(info.classId);
       b.i32_const(initialIdentityHash);
-      constants.instantiateConstant(function, b, nameConstant, stringType);
+      constants.instantiateConstant(b, nameConstant, stringType);
       b.struct_new(info.struct);
     });
   }
@@ -1005,12 +998,12 @@
     }
 
     return createConstant(constant, recordClassInfo.nonNullableType,
-        lazy: false, (function, b) {
+        lazy: false, (b) {
       b.i32_const(recordClassInfo.classId);
       b.i32_const(initialIdentityHash);
       for (Constant argument in arguments) {
         constants.instantiateConstant(
-            function, b, argument, translator.topInfo.nullableType);
+            b, argument, translator.topInfo.nullableType);
       }
       b.struct_new(recordClassInfo.struct);
     });
diff --git a/pkg/dart2wasm/lib/dynamic_forwarders.dart b/pkg/dart2wasm/lib/dynamic_forwarders.dart
index 9fec783..811c849 100644
--- a/pkg/dart2wasm/lib/dynamic_forwarders.dart
+++ b/pkg/dart2wasm/lib/dynamic_forwarders.dart
@@ -123,17 +123,14 @@
           translator.functions.getFunction(targetReference);
       b.local_get(receiverLocal);
       translator.convertType(
-          function, receiverLocal.type, targetFunction.type.inputs.first);
+          b, receiverLocal.type, targetFunction.type.inputs.first);
       b.call(targetFunction);
       // Box return value if needed
-      translator.convertType(function, targetFunction.type.outputs.single,
+      translator.convertType(b, targetFunction.type.outputs.single,
           _kind.functionType(translator).outputs.single);
     }, () {
-      generateNoSuchMethodCall(
-          translator,
-          function,
-          () => b.local_get(receiverLocal),
-          () => createGetterInvocationObject(translator, function, memberName));
+      generateNoSuchMethodCall(translator, b, () => b.local_get(receiverLocal),
+          () => createGetterInvocationObject(translator, b, memberName));
     });
 
     b.return_();
@@ -164,10 +161,10 @@
     }, () {
       generateNoSuchMethodCall(
           translator,
-          function,
+          b,
           () => b.local_get(receiverLocal),
           () => createSetterInvocationObject(
-              translator, function, memberName, positionalArgLocal));
+              translator, b, memberName, positionalArgLocal));
 
       b.drop(); // drop noSuchMethod return value
       b.local_get(positionalArgLocal);
@@ -185,12 +182,12 @@
     final positionalArgsLocal = function.locals[2]; // ref WasmArray
     final namedArgsLocal = function.locals[3]; // ref WasmArray
 
-    final classIdLocal = function.addLocal(w.NumType.i32);
+    final classIdLocal = b.addLocal(w.NumType.i32);
 
     // Continuation of this block calls `noSuchMethod` on the receiver.
     final noSuchMethodBlock = b.block();
 
-    final numArgsLocal = function.addLocal(w.NumType.i32);
+    final numArgsLocal = b.addLocal(w.NumType.i32);
 
     final methodSelectors =
         translator.dispatchTable.dynamicMethodSelectors(memberName);
@@ -277,13 +274,13 @@
         w.Local? adjustedPositionalArgsLocal;
         if (nRequired != nTotal) {
           adjustedPositionalArgsLocal =
-              function.addLocal(translator.nullableObjectArrayTypeRef);
+              b.addLocal(translator.nullableObjectArrayTypeRef);
           b.i32_const(nTotal);
           b.array_new_default(translator.nullableObjectArrayType);
           b.local_set(adjustedPositionalArgsLocal);
 
           // Copy passed arguments
-          final argIdxLocal = function.addLocal(w.NumType.i32);
+          final argIdxLocal = b.addLocal(w.NumType.i32);
           b.i32_const(0);
           b.local_set(argIdxLocal);
 
@@ -319,8 +316,8 @@
 
             b.local_get(adjustedPositionalArgsLocal);
             b.i32_const(optionalParamIdx);
-            translator.constants.instantiateConstant(
-                function, b, param, translator.topInfo.nullableType);
+            translator.constants
+                .instantiateConstant(b, param, translator.topInfo.nullableType);
             b.array_set(translator.nullableObjectArrayType);
             b.end();
           }
@@ -339,12 +336,12 @@
           b.br_if(noSuchMethodBlock);
         } else {
           adjustedNamedArgsLocal =
-              function.addLocal(translator.nullableObjectArrayTypeRef);
+              b.addLocal(translator.nullableObjectArrayTypeRef);
           b.i32_const(targetMemberParamInfo.named.length);
           b.array_new_default(translator.nullableObjectArrayType);
           b.local_set(adjustedNamedArgsLocal);
 
-          final namedParameterIdxLocal = function.addLocal(
+          final namedParameterIdxLocal = b.addLocal(
               translator.classInfo[translator.boxedIntClass]!.nullableType);
 
           final remainingNamedArgsLocal = numArgsLocal;
@@ -378,7 +375,6 @@
 
             b.local_get(namedArgsLocal);
             translator.constants.instantiateConstant(
-                function,
                 b,
                 SymbolConstant(name, null),
                 translator.classInfo[translator.symbolClass]!.nonNullableType);
@@ -411,7 +407,7 @@
               b.local_get(namedArgsLocal);
               b.local_get(namedParameterIdxLocal);
               translator.convertType(
-                  function, namedParameterIdxLocal.type, w.NumType.i64);
+                  b, namedParameterIdxLocal.type, w.NumType.i64);
               b.i32_wrap_i64();
               b.array_get(translator.nullableObjectArrayType);
 
@@ -427,14 +423,12 @@
               if (functionNodeDefaultValue != null) {
                 // Used by the member, has a default value
                 translator.constants.instantiateConstant(
-                    function,
                     b,
                     (functionNodeDefaultValue as ConstantExpression).constant,
                     translator.topInfo.nullableType);
               } else {
                 // Not used by the member
                 translator.constants.instantiateConstant(
-                  function,
                   b,
                   paramInfoDefaultValue!,
                   translator.topInfo.nullableType,
@@ -449,7 +443,7 @@
               b.local_get(namedArgsLocal);
               b.local_get(namedParameterIdxLocal);
               translator.convertType(
-                  function, namedParameterIdxLocal.type, w.NumType.i64);
+                  b, namedParameterIdxLocal.type, w.NumType.i64);
               b.i32_wrap_i64();
               b.array_get(translator.nullableObjectArrayType);
               b.array_set(translator.nullableObjectArrayType);
@@ -480,7 +474,7 @@
 
     final getterSelectors =
         translator.dispatchTable.dynamicGetterSelectors(memberName);
-    final getterValueLocal = function.addLocal(translator.topInfo.nullableType);
+    final getterValueLocal = b.addLocal(translator.topInfo.nullableType);
     for (final selector in getterSelectors) {
       for (final (:range, :target) in selector.targetRanges) {
         for (int classId = range.start; classId <= range.end; ++classId) {
@@ -513,9 +507,9 @@
           // Get field value
           b.local_get(receiverLocal);
           translator.convertType(
-              function, receiverLocal.type, targetFunction.type.inputs.first);
+              b, receiverLocal.type, targetFunction.type.inputs.first);
           b.call(targetFunction);
-          translator.convertType(function, targetFunction.type.outputs.single,
+          translator.convertType(b, targetFunction.type.outputs.single,
               translator.topInfo.nullableType);
           b.local_tee(getterValueLocal);
 
@@ -546,14 +540,14 @@
           final closureBaseType = w.RefType.def(
               translator.closureLayouter.closureBaseStruct,
               nullable: false);
-          final closureLocal = function.addLocal(closureBaseType);
+          final closureLocal = b.addLocal(closureBaseType);
           b.local_get(receiverLocal);
           b.ref_cast(closureBaseType);
           b.local_set(closureLocal);
 
           generateDynamicFunctionCall(
               translator,
-              function,
+              b,
               closureLocal,
               typeArgsLocal,
               positionalArgsLocal,
@@ -571,10 +565,10 @@
     // Unable to find a matching member, call `noSuchMethod`
     generateNoSuchMethodCall(
         translator,
-        function,
+        b,
         () => b.local_get(receiverLocal),
-        () => createInvocationObject(translator, function, memberName,
-            typeArgsLocal, positionalArgsLocal, namedArgsLocal));
+        () => createInvocationObject(translator, b, memberName, typeArgsLocal,
+            positionalArgsLocal, namedArgsLocal));
 
     b.end();
   }
@@ -616,7 +610,7 @@
 /// [noSuchMethodBlock] is used as the `br` target when the shape check fails.
 void generateDynamicFunctionCall(
   Translator translator,
-  w.FunctionBuilder function,
+  w.InstructionsBuilder b,
   w.Local closureLocal,
   w.Local typeArgsLocal,
   w.Local posArgsLocal,
@@ -627,11 +621,9 @@
   assert(posArgsLocal.type == translator.nullableObjectArrayTypeRef);
   assert(namedArgsLocal.type == translator.nullableObjectArrayTypeRef);
 
-  final b = function.body;
-
   // Read the `_FunctionType` field
   final functionTypeLocal =
-      function.addLocal(translator.closureLayouter.functionTypeType);
+      b.addLocal(translator.closureLayouter.functionTypeType);
   b.local_get(closureLocal);
   b.struct_get(translator.closureLayouter.closureBaseStruct,
       FieldIndex.closureRuntimeType);
@@ -691,17 +683,12 @@
 
 void createInvocationObject(
     Translator translator,
-    w.FunctionBuilder function,
+    w.InstructionsBuilder b,
     String memberName,
     w.Local typeArgsLocal,
     w.Local positionalArgsLocal,
     w.Local namedArgsLocal) {
-  final b = function.body;
-
-  translator.constants.instantiateConstant(
-      function,
-      b,
-      SymbolConstant(memberName, null),
+  translator.constants.instantiateConstant(b, SymbolConstant(memberName, null),
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
   b.local_get(typeArgsLocal);
@@ -719,15 +706,10 @@
 
 void createGetterInvocationObject(
   Translator translator,
-  w.FunctionBuilder function,
+  w.InstructionsBuilder b,
   String memberName,
 ) {
-  final b = function.body;
-
-  translator.constants.instantiateConstant(
-      function,
-      b,
-      SymbolConstant(memberName, null),
+  translator.constants.instantiateConstant(b, SymbolConstant(memberName, null),
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
   b.call(translator.functions
@@ -736,18 +718,13 @@
 
 void createSetterInvocationObject(
   Translator translator,
-  w.FunctionBuilder function,
+  w.InstructionsBuilder b,
   String memberName,
   w.Local positionalArgLocal,
 ) {
-  final b = function.body;
-
   memberName = '$memberName=';
 
-  translator.constants.instantiateConstant(
-      function,
-      b,
-      SymbolConstant(memberName, null),
+  translator.constants.instantiateConstant(b, SymbolConstant(memberName, null),
       translator.classInfo[translator.symbolClass]!.nonNullableType);
 
   b.local_get(positionalArgLocal);
@@ -757,12 +734,10 @@
 
 void generateNoSuchMethodCall(
   Translator translator,
-  w.FunctionBuilder function,
+  w.InstructionsBuilder b,
   void Function() pushReceiver,
   void Function() pushInvocationObject,
 ) {
-  final b = function.body;
-
   final SelectorInfo noSuchMethodSelector = translator.dispatchTable
       .selectorForTarget(translator.objectNoSuchMethod.reference);
   translator.functions.recordSelectorUse(noSuchMethodSelector);
@@ -775,7 +750,7 @@
 
   final invocationFactory = translator.functions
       .getFunction(translator.invocationGenericMethodFactory.reference);
-  translator.convertType(function, invocationFactory.type.outputs[0],
+  translator.convertType(b, invocationFactory.type.outputs[0],
       noSuchMethodSelector.signature.inputs[1]);
 
   // `noSuchMethod` can have extra parameters as long as they are optional.
@@ -786,10 +761,7 @@
       positionalArgIdx += 1) {
     final positionalParameterValue =
         noSuchMethodParamInfo.positional[positionalArgIdx]!;
-    translator.constants.instantiateConstant(
-        function,
-        b,
-        positionalParameterValue,
+    translator.constants.instantiateConstant(b, positionalParameterValue,
         noSuchMethodWasmFunctionType.inputs[wasmArgIdx]);
     wasmArgIdx += 1;
   }
@@ -798,7 +770,7 @@
   for (String namedParameterName in noSuchMethodParamInfo.names) {
     final namedParameterValue =
         noSuchMethodParamInfo.named[namedParameterName]!;
-    translator.constants.instantiateConstant(function, b, namedParameterValue,
+    translator.constants.instantiateConstant(b, namedParameterValue,
         noSuchMethodWasmFunctionType.inputs[wasmArgIdx]);
     wasmArgIdx += 1;
   }
diff --git a/pkg/dart2wasm/lib/globals.dart b/pkg/dart2wasm/lib/globals.dart
index 4ce65fb..e9a3897 100644
--- a/pkg/dart2wasm/lib/globals.dart
+++ b/pkg/dart2wasm/lib/globals.dart
@@ -149,7 +149,7 @@
         final global =
             m.globals.define(w.GlobalType(type, mutable: !field.isFinal));
         translator.constants
-            .instantiateConstant(null, global.initializer, init, type);
+            .instantiateConstant(global.initializer, init, type);
         global.initializer.end();
         return global;
       } else {
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 497f846..df7261e 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -164,7 +164,7 @@
 
     // int.bitlength
     if (cls == translator.coreTypes.intClass && name == 'bitLength') {
-      w.Local temp = codeGen.function.addLocal(w.NumType.i64);
+      w.Local temp = b.addLocal(w.NumType.i64);
       b.i64_const(64);
       codeGen.wrap(receiver, w.NumType.i64);
       b.local_tee(temp);
@@ -537,21 +537,21 @@
               .translateStorageType(types.rtt.typeRowDisplacementOffsetsType)
               .unpacked;
           translator.constants.instantiateConstant(
-              null, b, types.rtt.typeRowDisplacementOffsets, type);
+              b, types.rtt.typeRowDisplacementOffsets, type);
           return type;
         case "_typeRowDisplacementTable":
           final type = translator
               .translateStorageType(types.rtt.typeRowDisplacementTableType)
               .unpacked;
-          translator.constants.instantiateConstant(
-              null, b, types.rtt.typeRowDisplacementTable, type);
+          translator.constants
+              .instantiateConstant(b, types.rtt.typeRowDisplacementTable, type);
           return type;
         case "_typeRowDisplacementSubstTable":
           final type = translator
               .translateStorageType(types.rtt.typeRowDisplacementSubstTableType)
               .unpacked;
           translator.constants.instantiateConstant(
-              null, b, types.rtt.typeRowDisplacementSubstTable, type);
+              b, types.rtt.typeRowDisplacementSubstTable, type);
           return type;
         case "_typeNames":
           final type =
@@ -560,7 +560,7 @@
             b.ref_null((type as w.RefType).heapType);
           } else {
             translator.constants
-                .instantiateConstant(null, b, types.rtt.typeNames, type);
+                .instantiateConstant(b, types.rtt.typeNames, type);
           }
           return type;
       }
@@ -652,8 +652,8 @@
 
             final sourceArrayRefType =
                 w.RefType.def(arrayType, nullable: false);
-            final sourceArrayLocal = codeGen.addLocal(sourceArrayRefType);
-            final newArrayLocal = codeGen.addLocal(sourceArrayRefType);
+            final sourceArrayLocal = b.addLocal(sourceArrayRefType);
+            final newArrayLocal = b.addLocal(sourceArrayRefType);
 
             codeGen.wrap(sourceArray, sourceArrayRefType);
             b.local_tee(sourceArrayLocal);
@@ -1033,7 +1033,7 @@
             b.array_new_default(arrayType);
           } else {
             // Initialize to the provided value
-            w.Local lengthTemp = codeGen.addLocal(w.NumType.i32);
+            w.Local lengthTemp = b.addLocal(w.NumType.i32);
             b.local_set(lengthTemp);
             codeGen.wrap(initialValue, arrayType.elementType.type.unpacked);
             b.local_get(lengthTemp);
@@ -1164,8 +1164,7 @@
     ClassInfo receiverInfo = translator.classInfo[translator.listBaseClass]!;
     codeGen.wrap(
         node.arguments.positional.single, receiverInfo.nonNullableType);
-    w.Local receiverLocal =
-        codeGen.function.addLocal(receiverInfo.nonNullableType);
+    w.Local receiverLocal = b.addLocal(receiverInfo.nonNullableType);
     b.local_set(receiverLocal);
 
     ClassInfo newInfo = translator.classInfo[newClass]!;
@@ -1227,7 +1226,7 @@
       assert(receiver.name.text == "call");
       w.RefType receiverType =
           translator.translateType(dartTypeOf(receiver.receiver)) as w.RefType;
-      w.Local temp = codeGen.addLocal(receiverType);
+      w.Local temp = b.addLocal(receiverType);
       codeGen.wrap(receiver.receiver, receiverType);
       b.local_set(temp);
       w.FunctionType functionType = receiverType.heapType as w.FunctionType;
@@ -1256,7 +1255,7 @@
           [receiver.arguments.types.single]);
       w.RefType receiverType =
           translator.translateType(wasmFunctionType) as w.RefType;
-      w.Local tableIndex = codeGen.addLocal(w.NumType.i32);
+      w.Local tableIndex = b.addLocal(w.NumType.i32);
       codeGen.wrap(receiver.arguments.positional.single, w.NumType.i32);
       b.local_set(tableIndex);
       w.FunctionType functionType = receiverType.heapType as w.FunctionType;
@@ -1273,7 +1272,7 @@
   }
 
   /// Generate Wasm function for an intrinsic member.
-  bool generateMemberIntrinsic(Reference target, w.FunctionBuilder function,
+  bool generateMemberIntrinsic(Reference target, w.FunctionType functionType,
       List<w.Local> paramLocals, w.Label? returnLabel) {
     Member member = target.asMember;
     if (member is! Procedure) return false;
@@ -1309,7 +1308,7 @@
       ClassInfo boolInfo = translator.classInfo[translator.boxedBoolClass]!;
       ClassInfo intInfo = translator.classInfo[translator.boxedIntClass]!;
       ClassInfo doubleInfo = translator.classInfo[translator.boxedDoubleClass]!;
-      w.Local cid = function.addLocal(w.NumType.i32);
+      w.Local cid = b.addLocal(w.NumType.i32);
 
       // If the references are identical, return true.
       b.local_get(first);
@@ -1400,8 +1399,7 @@
     if (member.enclosingLibrary == translator.coreTypes.coreLibrary &&
         name == "identityHashCode") {
       final w.Local arg = paramLocals[0];
-      final w.Local nonNullArg =
-          function.addLocal(translator.topInfo.nonNullableType);
+      final w.Local nonNullArg = b.addLocal(translator.topInfo.nonNullableType);
       final List<int> classIds = translator.valueClasses.keys
           .map((cls) => translator.classInfo[cls]!.classId)
           .toList()
@@ -1454,7 +1452,7 @@
       Class cls = member.enclosingClass!;
       ClassInfo classInfo = translator.classInfo[cls]!;
       w.ArrayType arrayType =
-          (function.type.outputs.single as w.RefType).heapType as w.ArrayType;
+          (functionType.outputs.single as w.RefType).heapType as w.ArrayType;
       w.Local object = paramLocals[0];
       w.Local preciseObject = codeGen.addLocal(classInfo.nonNullableType);
       b.local_get(object);
@@ -1478,63 +1476,63 @@
         CodeGenCallback? code = _unaryOperatorMap[intType]![op];
         if (code != null) {
           w.ValueType resultType = _unaryResultMap[op] ?? intType;
-          w.ValueType inputType = function.type.inputs.single;
-          w.ValueType outputType = function.type.outputs.single;
-          b.local_get(function.locals[0]);
-          translator.convertType(function, inputType, intType);
+          w.ValueType inputType = functionType.inputs.single;
+          w.ValueType outputType = functionType.outputs.single;
+          b.local_get(paramLocals[0]);
+          translator.convertType(b, inputType, intType);
           code(codeGen);
-          translator.convertType(function, resultType, outputType);
+          translator.convertType(b, resultType, outputType);
           return true;
         }
       } else if (functionNode.requiredParameterCount == 1) {
         CodeGenCallback? code = _binaryOperatorMap[intType]![intType]![op];
         if (code != null) {
-          w.ValueType leftType = function.type.inputs[0];
-          w.ValueType rightType = function.type.inputs[1];
-          w.ValueType outputType = function.type.outputs.single;
+          w.ValueType leftType = functionType.inputs[0];
+          w.ValueType rightType = functionType.inputs[1];
+          w.ValueType outputType = functionType.outputs.single;
           if (rightType == intType) {
             // int parameter
-            b.local_get(function.locals[0]);
-            translator.convertType(function, leftType, intType);
-            b.local_get(function.locals[1]);
+            b.local_get(paramLocals[0]);
+            translator.convertType(b, leftType, intType);
+            b.local_get(paramLocals[1]);
             code(codeGen);
             if (!isComparison(op)) {
-              translator.convertType(function, intType, outputType);
+              translator.convertType(b, intType, outputType);
             }
             return true;
           }
           // num parameter
           ClassInfo intInfo = translator.classInfo[translator.boxedIntClass]!;
           w.Label intArg = b.block(const [], [intInfo.nonNullableType]);
-          b.local_get(function.locals[1]);
-          b.br_on_cast(intArg, function.locals[1].type as w.RefType,
+          b.local_get(paramLocals[1]);
+          b.br_on_cast(intArg, paramLocals[1].type as w.RefType,
               intInfo.nonNullableType);
           // double argument
           b.drop();
-          b.local_get(function.locals[0]);
-          translator.convertType(function, leftType, intType);
+          b.local_get(paramLocals[0]);
+          translator.convertType(b, leftType, intType);
           b.f64_convert_i64_s();
-          b.local_get(function.locals[1]);
-          translator.convertType(function, rightType, doubleType);
+          b.local_get(paramLocals[1]);
+          translator.convertType(b, rightType, doubleType);
           // Inline double op
           CodeGenCallback doubleCode =
               _binaryOperatorMap[doubleType]![doubleType]![op]!;
           doubleCode(codeGen);
           if (!isComparison(op)) {
-            translator.convertType(function, doubleType, outputType);
+            translator.convertType(b, doubleType, outputType);
           }
           b.return_();
           b.end();
           // int argument
-          translator.convertType(function, intInfo.nonNullableType, intType);
-          w.Local rightTemp = function.addLocal(intType);
+          translator.convertType(b, intInfo.nonNullableType, intType);
+          w.Local rightTemp = b.addLocal(intType);
           b.local_set(rightTemp);
-          b.local_get(function.locals[0]);
-          translator.convertType(function, leftType, intType);
+          b.local_get(paramLocals[0]);
+          translator.convertType(b, leftType, intType);
           b.local_get(rightTemp);
           code(codeGen);
           if (!isComparison(op)) {
-            translator.convertType(function, intType, outputType);
+            translator.convertType(b, intType, outputType);
           }
           return true;
         }
@@ -1549,12 +1547,12 @@
         CodeGenCallback? code = _unaryOperatorMap[doubleType]![op];
         if (code != null) {
           w.ValueType resultType = _unaryResultMap[op] ?? doubleType;
-          w.ValueType inputType = function.type.inputs.single;
-          w.ValueType outputType = function.type.outputs.single;
-          b.local_get(function.locals[0]);
-          translator.convertType(function, inputType, doubleType);
+          w.ValueType inputType = functionType.inputs.single;
+          w.ValueType outputType = functionType.outputs.single;
+          b.local_get(paramLocals[0]);
+          translator.convertType(b, inputType, doubleType);
           code(codeGen);
-          translator.convertType(function, resultType, outputType);
+          translator.convertType(b, resultType, outputType);
           return true;
         }
       }
@@ -1562,26 +1560,26 @@
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_isInstantiationClosure") {
-      assert(function.locals.length == 1);
-      b.local_get(function.locals[0]); // ref _Closure
+      assert(paramLocals.length == 1);
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitInstantiationClosureCheck(translator);
       return true;
     }
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_instantiatedClosure") {
-      assert(function.locals.length == 1);
-      b.local_get(function.locals[0]); // ref _Closure
+      assert(paramLocals.length == 1);
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitGetInstantiatedClosure(translator);
       return true;
     }
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_instantiationClosureTypeHash") {
-      assert(function.locals.length == 1);
+      assert(paramLocals.length == 1);
 
       // Instantiation context, to be passed to the hash function.
-      b.local_get(function.locals[0]); // ref _Closure
+      b.local_get(paramLocals[0]); // ref _Closure
       b.ref_cast(w.RefType(translator.closureLayouter.closureBaseStruct,
           nullable: false));
       b.struct_get(translator.closureLayouter.closureBaseStruct,
@@ -1591,7 +1589,7 @@
           nullable: false));
 
       // Hash function.
-      b.local_get(function.locals[0]); // ref _Closure
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitGetInstantiatedClosure(translator);
       b.emitGetClosureVtable(translator);
       b.ref_cast(w.RefType.def(
@@ -1607,7 +1605,7 @@
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_instantiationClosureTypeEquals") {
-      assert(function.locals.length == 2);
+      assert(paramLocals.length == 2);
 
       final w.StructType closureBaseStruct =
           translator.closureLayouter.closureBaseStruct;
@@ -1616,17 +1614,17 @@
           translator.closureLayouter.instantiationContextBaseStruct,
           nullable: false);
 
-      b.local_get(function.locals[0]); // ref _Closure
+      b.local_get(paramLocals[0]); // ref _Closure
       b.ref_cast(w.RefType(closureBaseStruct, nullable: false));
       b.struct_get(closureBaseStruct, FieldIndex.closureContext);
       b.ref_cast(instantiationContextBase);
 
-      b.local_get(function.locals[1]); // ref _Closure
+      b.local_get(paramLocals[1]); // ref _Closure
       b.ref_cast(w.RefType(closureBaseStruct, nullable: false));
       b.struct_get(closureBaseStruct, FieldIndex.closureContext);
       b.ref_cast(instantiationContextBase);
 
-      b.local_get(function.locals[0]);
+      b.local_get(paramLocals[0]);
       b.emitGetInstantiatedClosure(translator);
       b.emitGetClosureVtable(translator);
       b.ref_cast(w.RefType.def(
@@ -1643,55 +1641,53 @@
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_isInstanceTearOff") {
-      assert(function.locals.length == 1);
-      b.local_get(function.locals[0]); // ref _Closure
+      assert(paramLocals.length == 1);
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitTearOffCheck(translator);
       return true;
     }
 
     if (member.enclosingClass == translator.closureClass &&
         name == "_instanceTearOffReceiver") {
-      assert(function.locals.length == 1);
-      b.local_get(function.locals[0]); // ref _Closure
+      assert(paramLocals.length == 1);
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitGetTearOffReceiver(translator);
       return true;
     }
 
     if (member.enclosingClass == translator.closureClass && name == "_vtable") {
-      assert(function.locals.length == 1);
-      b.local_get(function.locals[0]); // ref _Closure
+      assert(paramLocals.length == 1);
+      b.local_get(paramLocals[0]); // ref _Closure
       b.emitGetClosureVtable(translator);
       return true;
     }
 
     if (member.enclosingClass == translator.coreTypes.functionClass &&
         name == "apply") {
-      assert(function.type.inputs.length == 3);
+      assert(functionType.inputs.length == 3);
 
-      final closureLocal = function.locals[0]; // ref #ClosureBase
-      final posArgsNullableLocal = function.locals[1]; // ref null Object
-      final namedArgsLocal = function.locals[2]; // ref null Object
+      final closureLocal = paramLocals[0]; // ref #ClosureBase
+      final posArgsNullableLocal = paramLocals[1]; // ref null Object
+      final namedArgsLocal = paramLocals[2]; // ref null Object
 
       // Create empty type arguments array.
-      final typeArgsLocal = function.addLocal(translator.makeArray(function,
-          translator.typeArrayType, 0, (elementType, elementIndex) {}));
+      final typeArgsLocal = b.addLocal(translator.makeArray(
+          b, translator.typeArrayType, 0, (elementType, elementIndex) {}));
       b.local_set(typeArgsLocal);
 
       // Create empty list for positional args if the argument is null
-      final posArgsLocal =
-          function.addLocal(translator.nullableObjectArrayTypeRef);
+      final posArgsLocal = b.addLocal(translator.nullableObjectArrayTypeRef);
       b.local_get(posArgsNullableLocal);
       b.ref_is_null();
 
       b.if_([], [translator.nullableObjectArrayTypeRef]);
       translator.makeArray(
-          function, translator.nullableObjectArrayType, 0, (_, __) {});
+          b, translator.nullableObjectArrayType, 0, (_, __) {});
 
       b.else_();
       // List argument may be a custom list type, convert it to `WasmListBase`
       // with `WasmListBase.of`.
       translator.constants.instantiateConstant(
-        function,
         b,
         TypeLiteralConstant(DynamicType()),
         translator.types.nonNullableTypeType,
@@ -1706,33 +1702,33 @@
       // Convert named argument map to list, to be passed to shape and type
       // checkers and the dynamic call entry.
       final namedArgsListLocal =
-          function.addLocal(translator.nullableObjectArrayTypeRef);
+          b.addLocal(translator.nullableObjectArrayTypeRef);
       b.local_get(namedArgsLocal);
       codeGen.call(translator.namedParameterMapToArray.reference);
       b.local_set(namedArgsListLocal);
 
       final noSuchMethodBlock = b.block();
 
-      generateDynamicFunctionCall(translator, function, closureLocal,
-          typeArgsLocal, posArgsLocal, namedArgsListLocal, noSuchMethodBlock);
+      generateDynamicFunctionCall(translator, b, closureLocal, typeArgsLocal,
+          posArgsLocal, namedArgsListLocal, noSuchMethodBlock);
       b.return_();
 
       b.end(); // noSuchMethodBlock
 
       generateNoSuchMethodCall(
           translator,
-          function,
+          b,
           () => b.local_get(closureLocal),
-          () => createInvocationObject(translator, function, "call",
-              typeArgsLocal, posArgsLocal, namedArgsListLocal));
+          () => createInvocationObject(translator, b, "call", typeArgsLocal,
+              posArgsLocal, namedArgsListLocal));
 
       return true;
     }
 
     // Error._throw
     if (member.enclosingClass == translator.errorClass && name == "_throw") {
-      final objectLocal = function.locals[0]; // ref #Top
-      final stackTraceLocal = function.locals[1]; // ref Object
+      final objectLocal = paramLocals[0]; // ref #Top
+      final stackTraceLocal = paramLocals[1]; // ref Object
 
       final notErrorBlock = b.block([], [objectLocal.type]);
 
@@ -1744,7 +1740,7 @@
       b.br_on_cast_fail(
           notErrorBlock, objectLocal.type as w.RefType, errorRefType);
 
-      final errorLocal = function.addLocal(errorRefType);
+      final errorLocal = b.addLocal(errorRefType);
       b.local_tee(errorLocal);
 
       b.struct_get(errorClassInfo.struct, stackTraceFieldIndex);
diff --git a/pkg/dart2wasm/lib/state_machine.dart b/pkg/dart2wasm/lib/state_machine.dart
index 29bef15..cc0d292 100644
--- a/pkg/dart2wasm/lib/state_machine.dart
+++ b/pkg/dart2wasm/lib/state_machine.dart
@@ -7,6 +7,7 @@
 
 import 'closures.dart';
 import 'code_generator.dart';
+import 'translator.dart';
 
 /// Placement of a control flow graph target within a statement. This
 /// distinction is necessary since some statements need to have two targets
@@ -308,12 +309,13 @@
   /// Call this right before terminating a CFG block.
   void _terminateTryBlocks() {
     int nextHandlerIdx = _handlers.length - 1;
+    final b = codeGen.b;
     for (final int nCoveredHandlers in _tryBlockNumHandlers.reversed) {
-      final stackTraceLocal = codeGen
-          .addLocal(codeGen.translator.stackTraceInfo.repr.nonNullableType);
+      final stackTraceLocal =
+          b.addLocal(codeGen.translator.stackTraceInfo.repr.nonNullableType);
 
       final exceptionLocal =
-          codeGen.addLocal(codeGen.translator.topInfo.nonNullableType);
+          b.addLocal(codeGen.translator.topInfo.nonNullableType);
 
       void generateCatchBody() {
         // Set continuations of finalizers that can be reached by this `catch`
@@ -321,26 +323,25 @@
         for (int i = 0; i < nCoveredHandlers; i += 1) {
           final handler = _handlers[nextHandlerIdx - i];
           if (handler is Finalizer) {
-            handler.setContinuationRethrow(
-                () => codeGen.b.local_get(exceptionLocal),
-                () => codeGen.b.local_get(stackTraceLocal));
+            handler.setContinuationRethrow(() => b.local_get(exceptionLocal),
+                () => b.local_get(stackTraceLocal));
           }
         }
 
         // Set the untyped "current exception" variable. Catch blocks will do the
         // type tests as necessary using this variable and set their exception
         // and stack trace locals.
-        codeGen.setSuspendStateCurrentException(
-            () => codeGen.b.local_get(exceptionLocal));
+        codeGen
+            .setSuspendStateCurrentException(() => b.local_get(exceptionLocal));
         codeGen.setSuspendStateCurrentStackTrace(
-            () => codeGen.b.local_get(stackTraceLocal));
+            () => b.local_get(stackTraceLocal));
 
         codeGen._jumpToTarget(_handlers[nextHandlerIdx].target);
       }
 
-      codeGen.b.catch_(codeGen.translator.exceptionTag);
-      codeGen.b.local_set(stackTraceLocal);
-      codeGen.b.local_set(exceptionLocal);
+      b.catch_(codeGen.translator.exceptionTag);
+      b.local_set(stackTraceLocal);
+      b.local_set(exceptionLocal);
 
       generateCatchBody();
 
@@ -355,21 +356,21 @@
       }
 
       if (canHandleJSExceptions) {
-        codeGen.b.catch_all();
+        b.catch_all();
 
         // We can't inspect the thrown object in a `catch_all` and get a stack
         // trace, so we just attach the current stack trace.
         codeGen.call(codeGen.translator.stackTraceCurrent.reference);
-        codeGen.b.local_set(stackTraceLocal);
+        b.local_set(stackTraceLocal);
 
         // We create a generic JavaScript error.
         codeGen.call(codeGen.translator.javaScriptErrorFactory.reference);
-        codeGen.b.local_set(exceptionLocal);
+        b.local_set(exceptionLocal);
 
         generateCatchBody();
       }
 
-      codeGen.b.end(); // end catch
+      b.end(); // end catch
 
       nextHandlerIdx -= nCoveredHandlers;
     }
@@ -568,7 +569,12 @@
 ///
 /// This is used to compile `async` and `sync*` functions.
 abstract class StateMachineCodeGenerator extends CodeGenerator {
-  StateMachineCodeGenerator(super.translator, super.function, super.reference);
+  w.FunctionBuilder function;
+
+  StateMachineCodeGenerator(
+      Translator translator, this.function, Reference reference)
+      : super(translator, function.type, function.body, reference,
+            paramLocals: function.locals.toList());
 
   /// Targets of the CFG, indexed by target index.
   late final List<StateTarget> targets;
@@ -615,12 +621,11 @@
   }
 
   @override
-  w.BaseFunction generateLambda(Lambda lambda, Closures closures) {
+  void generateLambda(Lambda lambda, Closures closures) {
     this.closures = closures;
     setSourceMapSource(lambda.functionNodeSource);
     setupLambdaParametersAndContexts(lambda);
     _generateBodies(lambda.functionNode);
-    return function;
   }
 
   void _generateBodies(FunctionNode functionNode) {
@@ -832,8 +837,8 @@
       b.local_get(switchValueNullableLocal);
       b.ref_as_non_null();
       // Unbox if necessary
-      translator.convertType(function, switchValueNullableLocal.type,
-          switchValueNonNullableLocal.type);
+      translator.convertType(
+          b, switchValueNullableLocal.type, switchValueNonNullableLocal.type);
       b.local_set(switchValueNonNullableLocal);
     }
 
@@ -950,7 +955,7 @@
       setVariable(catch_.exception!, () {
         getSuspendStateCurrentException();
         // Type test already passed, convert the exception.
-        translator.convertType(function, translator.topInfo.nullableType,
+        translator.convertType(b, translator.topInfo.nullableType,
             translator.translateType(catch_.exception!.type));
       });
       setVariable(catch_.stackTrace!, () => getSuspendStateCurrentStackTrace());
@@ -1215,6 +1220,6 @@
   /// Same as [_getVariable], but boxes the value if it's not already boxed.
   void _getVariableBoxed(VariableDeclaration variable) {
     final varType = _getVariable(variable);
-    translator.convertType(function, varType, translator.topInfo.nullableType);
+    translator.convertType(b, varType, translator.topInfo.nullableType);
   }
 }
diff --git a/pkg/dart2wasm/lib/sync_star.dart b/pkg/dart2wasm/lib/sync_star.dart
index 3e8eece..38ff310 100644
--- a/pkg/dart2wasm/lib/sync_star.dart
+++ b/pkg/dart2wasm/lib/sync_star.dart
@@ -142,17 +142,19 @@
     // Set the current Wasm function for the code generator to the inner
     // function of the `sync*`, which is to contain the body.
     function = resumeFun;
+    b = resumeFun.body;
+    functionType = resumeFun.type;
 
     // Set up locals for contexts and `this`.
     thisLocal = null;
     Context? localContext = context;
     while (localContext != null) {
       if (!localContext.isEmpty) {
-        localContext.currentLocal = function
-            .addLocal(w.RefType.def(localContext.struct, nullable: true));
+        localContext.currentLocal =
+            b.addLocal(w.RefType.def(localContext.struct, nullable: true));
         if (localContext.containsThis) {
           assert(thisLocal == null);
-          thisLocal = function.addLocal(localContext
+          thisLocal = b.addLocal(localContext
               .struct.fields[localContext.thisFieldIndex].type.unpacked
               .withNullability(false));
           translator.globals.instantiateDummyValue(b, thisLocal!.type);
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 45b222b..ecb0bc9 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -374,8 +374,8 @@
       codeGen.generate();
 
       if (options.printWasm) {
-        print(codeGen.function.type);
-        print(codeGen.function.body.trace);
+        print(function.type);
+        print(function.body.trace);
       }
 
       // The constructor allocator, initializer, and body functions all
@@ -385,10 +385,11 @@
       // constructor initializer methods are generated.
       if (member is! Constructor || reference.isInitializerReference) {
         for (Lambda lambda in codeGen.closures.lambdas.values) {
-          w.BaseFunction lambdaFunction = CodeGenerator.forFunction(
-                  this, lambda.functionNode, lambda.function, reference)
+          final lambdaFunction = lambda.function;
+          CodeGenerator.forFunction(
+                  this, lambda.functionNode, lambdaFunction, reference)
               .generateLambda(lambda, codeGen.closures);
-          _printFunction(lambdaFunction, "$canonicalName (closure)");
+          _printFunction(function, "$canonicalName (closure)");
         }
       }
 
@@ -826,9 +827,7 @@
     return (from == voidMarker) ^ (to == voidMarker) || !from.isSubtypeOf(to);
   }
 
-  void convertType(
-      w.FunctionBuilder function, w.ValueType from, w.ValueType to) {
-    final b = function.body;
+  void convertType(w.InstructionsBuilder b, w.ValueType from, w.ValueType to) {
     if (from == voidMarker || to == voidMarker) {
       if (from != voidMarker) {
         b.drop();
@@ -856,7 +855,7 @@
         // Boxing
         ClassInfo info = classInfo[boxedClasses[from]!]!;
         assert(info.struct.isSubtypeOf(to.heapType));
-        w.Local temp = function.addLocal(from);
+        w.Local temp = b.addLocal(from);
         b.local_set(temp);
         b.i32_const(info.classId);
         b.local_get(temp);
@@ -1117,10 +1116,8 @@
     return null;
   }
 
-  w.ValueType makeArray(w.FunctionBuilder function, w.ArrayType arrayType,
+  w.ValueType makeArray(w.InstructionsBuilder b, w.ArrayType arrayType,
       int length, void Function(w.ValueType, int) generateItem) {
-    final b = function.body;
-
     final w.ValueType elementType = arrayType.elementType.type.unpacked;
     final arrayTypeRef = w.RefType.def(arrayType, nullable: false);
 
@@ -1129,7 +1126,7 @@
       b.i32_const(length);
       b.array_new_default(arrayType);
       if (length > 0) {
-        final w.Local arrayLocal = function.addLocal(arrayTypeRef);
+        final w.Local arrayLocal = b.addLocal(arrayTypeRef);
         b.local_set(arrayLocal);
         for (int i = 0; i < length; i++) {
           b.local_get(arrayLocal);
@@ -1216,7 +1213,7 @@
       w.Local receiver = trampoline.locals[0];
       b.local_get(receiver);
       translator.convertType(
-          trampoline, receiver.type, target.type.inputs[targetIndex++]);
+          b, receiver.type, target.type.inputs[targetIndex++]);
     }
     int argIndex = 1;
     for (int i = 0; i < typeCount; i++) {
@@ -1227,11 +1224,10 @@
       if (i < posArgCount) {
         w.Local arg = trampoline.locals[argIndex++];
         b.local_get(arg);
-        translator.convertType(
-            trampoline, arg.type, target.type.inputs[targetIndex++]);
+        translator.convertType(b, arg.type, target.type.inputs[targetIndex++]);
       } else {
-        translator.constants.instantiateConstant(trampoline, b,
-            paramInfo.positional[i]!, target.type.inputs[targetIndex++]);
+        translator.constants.instantiateConstant(
+            b, paramInfo.positional[i]!, target.type.inputs[targetIndex++]);
       }
     }
     int argNameIndex = 0;
@@ -1240,12 +1236,11 @@
       if (argNameIndex < argNames.length && argNames[argNameIndex] == argName) {
         w.Local arg = trampoline.locals[argIndex++];
         b.local_get(arg);
-        translator.convertType(
-            trampoline, arg.type, target.type.inputs[targetIndex++]);
+        translator.convertType(b, arg.type, target.type.inputs[targetIndex++]);
         argNameIndex++;
       } else {
-        translator.constants.instantiateConstant(trampoline, b,
-            paramInfo.named[argName]!, target.type.inputs[targetIndex++]);
+        translator.constants.instantiateConstant(
+            b, paramInfo.named[argName]!, target.type.inputs[targetIndex++]);
       }
     }
     assert(argIndex == trampoline.type.inputs.length);
@@ -1254,9 +1249,7 @@
 
     b.call(target);
 
-    translator.convertType(
-        trampoline,
-        translator.outputOrVoid(target.type.outputs),
+    translator.convertType(b, translator.outputOrVoid(target.type.outputs),
         translator.outputOrVoid(trampoline.type.outputs));
     b.end();
   }
@@ -1308,11 +1301,10 @@
 
       // Get context, downcast it to expected type
       b.local_get(closureLocal);
-      translator.convertType(function, closureLocal.type, closureBaseType);
+      translator.convertType(b, closureLocal.type, closureBaseType);
       b.struct_get(translator.closureLayouter.closureBaseStruct,
           FieldIndex.closureContext);
-      translator.convertType(
-          function, closureContextType, targetInputs[inputIdx]);
+      translator.convertType(b, closureContextType, targetInputs[inputIdx]);
       inputIdx += 1;
     }
 
@@ -1322,7 +1314,7 @@
       b.i32_const(typeIdx);
       b.array_get(translator.typeArrayType);
       translator.convertType(
-          function, translator.topInfo.nullableType, targetInputs[inputIdx]);
+          b, translator.topInfo.nullableType, targetInputs[inputIdx]);
       inputIdx += 1;
     }
 
@@ -1344,12 +1336,12 @@
         b.i32_const(posIdx);
         b.array_get(translator.nullableObjectArrayType);
         b.else_();
-        translator.constants.instantiateConstant(function, b,
-            paramInfo.positional[posIdx]!, translator.topInfo.nullableType);
+        translator.constants.instantiateConstant(
+            b, paramInfo.positional[posIdx]!, translator.topInfo.nullableType);
         b.end();
       }
       translator.convertType(
-          function, translator.topInfo.nullableType, targetInputs[inputIdx]);
+          b, translator.topInfo.nullableType, targetInputs[inputIdx]);
       inputIdx += 1;
     }
 
@@ -1364,7 +1356,7 @@
       return null;
     }
 
-    final namedArgValueIndexLocal = function
+    final namedArgValueIndexLocal = b
         .addLocal(translator.classInfo[translator.boxedIntClass]!.nullableType);
 
     for (String paramName in paramInfo.names) {
@@ -1375,7 +1367,6 @@
       // Get passed value
       b.local_get(namedArgsListLocal);
       translator.constants.instantiateConstant(
-          function,
           b,
           SymbolConstant(paramName, null),
           translator.classInfo[translator.symbolClass]!.nonNullableType);
@@ -1387,12 +1378,11 @@
         // Shape check passed, parameter must be passed
         b.local_get(namedArgsListLocal);
         b.local_get(namedArgValueIndexLocal);
-        translator.convertType(
-            function, namedArgValueIndexLocal.type, w.NumType.i64);
+        translator.convertType(b, namedArgValueIndexLocal.type, w.NumType.i64);
         b.i32_wrap_i64();
         b.array_get(translator.nullableObjectArrayType);
         translator.convertType(
-            function,
+            b,
             translator.nullableObjectArrayType.elementType.type.unpacked,
             target.type.inputs[inputIdx]);
       } else {
@@ -1403,14 +1393,12 @@
         if (functionNodeDefaultValue != null) {
           // Used by the member, has a default value
           translator.constants.instantiateConstant(
-              function,
               b,
               (functionNodeDefaultValue as ConstantExpression).constant,
               translator.topInfo.nullableType);
         } else {
           // Not used by the member
           translator.constants.instantiateConstant(
-            function,
             b,
             paramInfoDefaultValue!,
             translator.topInfo.nullableType,
@@ -1419,22 +1407,19 @@
         b.else_(); // value index not null
         b.local_get(namedArgsListLocal);
         b.local_get(namedArgValueIndexLocal);
-        translator.convertType(
-            function, namedArgValueIndexLocal.type, w.NumType.i64);
+        translator.convertType(b, namedArgValueIndexLocal.type, w.NumType.i64);
         b.i32_wrap_i64();
         b.array_get(translator.nullableObjectArrayType);
         b.end();
         translator.convertType(
-            function, translator.topInfo.nullableType, targetInputs[inputIdx]);
+            b, translator.topInfo.nullableType, targetInputs[inputIdx]);
       }
       inputIdx += 1;
     }
 
     b.call(target);
 
-    translator.convertType(
-        function,
-        translator.outputOrVoid(target.type.outputs),
+    translator.convertType(b, translator.outputOrVoid(target.type.outputs),
         translator.outputOrVoid(function.type.outputs));
 
     b.end(); // end function
@@ -1503,8 +1488,8 @@
           ),
           name);
       final b = function.body;
-      translator.constants.instantiateConstant(function, b,
-          TypeLiteralConstant(type), translator.types.nonNullableTypeType);
+      translator.constants.instantiateConstant(
+          b, TypeLiteralConstant(type), translator.types.nonNullableTypeType);
       for (int i = 1; i < wasmTarget.type.inputs.length; ++i) {
         b.local_get(b.locals[i - 1]);
       }
@@ -1531,10 +1516,10 @@
           ),
           name);
       final b = function.body;
-      translator.constants.instantiateConstant(function, b,
-          TypeLiteralConstant(type1), translator.types.nonNullableTypeType);
-      translator.constants.instantiateConstant(function, b,
-          TypeLiteralConstant(type2), translator.types.nonNullableTypeType);
+      translator.constants.instantiateConstant(
+          b, TypeLiteralConstant(type1), translator.types.nonNullableTypeType);
+      translator.constants.instantiateConstant(
+          b, TypeLiteralConstant(type2), translator.types.nonNullableTypeType);
       for (int i = 2; i < wasmTarget.type.inputs.length; ++i) {
         b.local_get(b.locals[i - 2]);
       }
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index 6032c0a..e70b8b2 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -168,7 +168,7 @@
   /// stack.
   void _makeTypeArray(CodeGenerator codeGen, Iterable<DartType> types) {
     if (types.every(isTypeConstant)) {
-      translator.constants.instantiateConstant(codeGen.function, codeGen.b,
+      translator.constants.instantiateConstant(codeGen.b,
           translator.constants.makeTypeArray(types), typeArrayExpectedType);
     } else {
       for (DartType type in types) {
@@ -194,7 +194,7 @@
         type.named.map((t) => StringConstant(t.name)).toList());
 
     translator.constants.instantiateConstant(
-        codeGen.function, codeGen.b, names, recordTypeNamesFieldExpectedType);
+        codeGen.b, names, recordTypeNamesFieldExpectedType);
     _makeTypeArray(
         codeGen, type.positional.followedBy(type.named.map((t) => t.type)));
   }
@@ -267,7 +267,6 @@
     // WasmArray<_NamedParameter> namedParameters
     if (type.namedParameters.every((n) => isTypeConstant(n.type))) {
       translator.constants.instantiateConstant(
-          codeGen.function,
           b,
           translator.constants.makeNamedParametersArray(type),
           namedParametersExpectedType);
@@ -291,8 +290,8 @@
       }
       w.ValueType namedParametersListType =
           codeGen.makeArrayFromExpressions(expressions, namedParameterType);
-      translator.convertType(codeGen.function, namedParametersListType,
-          namedParametersExpectedType);
+      translator.convertType(
+          b, namedParametersListType, namedParametersExpectedType);
     }
   }
 
@@ -305,7 +304,7 @@
     final b = codeGen.b;
     if (isTypeConstant(type)) {
       translator.constants.instantiateConstant(
-          codeGen.function, b, TypeLiteralConstant(type), nonNullableTypeType);
+          b, TypeLiteralConstant(type), nonNullableTypeType);
       return nonNullableTypeType;
     }
     // All of the singleton types represented by canonical objects should be
@@ -385,8 +384,7 @@
     b.comment("type check against $testedAgainstType");
     w.Local? operandTemp;
     if (translator.options.verifyTypeChecks) {
-      operandTemp =
-          b.addLocal(translator.topInfo.nullableType, isParameter: false);
+      operandTemp = b.addLocal(translator.topInfo.nullableType);
       b.local_tee(operandTemp);
     }
     final (typeToCheck, :checkArguments) =
@@ -431,8 +429,8 @@
       if (location != null) {
         w.FunctionType verifyFunctionType = translator.signatureForDirectCall(
             translator.verifyOptimizedTypeCheck.reference);
-        translator.constants.instantiateConstant(codeGen.function, b,
-            StringConstant('$location'), verifyFunctionType.inputs.last);
+        translator.constants.instantiateConstant(
+            b, StringConstant('$location'), verifyFunctionType.inputs.last);
       } else {
         b.ref_null(w.HeapType.none);
       }
@@ -458,7 +456,7 @@
       return translator.translateType(testedAgainstType);
     }
 
-    w.Local operand = b.addLocal(boxedOperandType, isParameter: false);
+    w.Local operand = b.addLocal(boxedOperandType);
     b.local_tee(operand);
 
     late List<w.ValueType> outputsToDrop;
@@ -609,7 +607,7 @@
       final b = function.body;
 
       w.Local operand = b.locals[0];
-      w.Local boolTemp = function.addLocal(w.NumType.i32);
+      w.Local boolTemp = b.addLocal(w.NumType.i32);
 
       final w.Label resultLabel = b.block(const [], const [w.NumType.i32]);
       if (operandIsNullable) {
@@ -617,7 +615,7 @@
         b.local_get(operand);
         b.br_on_null(nullLabel);
         final nonNullableOperand =
-            function.addLocal(translator.topInfo.nonNullableType);
+            b.addLocal(translator.topInfo.nonNullableType);
         b.local_get(operand);
         b.ref_cast(nonNullableOperand.type as w.RefType);
         b.local_set(nonNullableOperand);
@@ -644,7 +642,7 @@
         // Otherwise we have to check each argument.
 
         // Call Object._getArguments()
-        w.Local typeArguments = function.addLocal(typeArrayExpectedType);
+        w.Local typeArguments = b.addLocal(typeArrayExpectedType);
         b.local_get(operand);
         b.call(translator.functions
             .getFunction(translator.objectGetTypeArguments.reference));
@@ -778,8 +776,8 @@
         }
       } else {
         b.local_get(b.locals[0]);
-        translator.constants.instantiateConstant(function, b,
-            TypeLiteralConstant(testedAgainstType), nonNullableTypeType);
+        translator.constants.instantiateConstant(
+            b, TypeLiteralConstant(testedAgainstType), nonNullableTypeType);
         b.call(translator.functions
             .getFunction(translator.throwAsCheckError.reference));
       }
@@ -788,7 +786,7 @@
       b.end();
 
       b.local_get(b.locals[0]);
-      translator.convertType(function, argumentType, returnType);
+      translator.convertType(b, argumentType, returnType);
       b.return_();
       b.end();
 
diff --git a/pkg/wasm_builder/lib/src/builder/function.dart b/pkg/wasm_builder/lib/src/builder/function.dart
index 4fe8eca..77b5554 100644
--- a/pkg/wasm_builder/lib/src/builder/function.dart
+++ b/pkg/wasm_builder/lib/src/builder/function.dart
@@ -15,16 +15,9 @@
 
   FunctionBuilder(ModuleBuilder module, super.index, super.type,
       [super.functionName]) {
-    body = InstructionsBuilder(module, type.outputs);
-    for (ir.ValueType paramType in type.inputs) {
-      body.addLocal(paramType, isParameter: true);
-    }
+    body = InstructionsBuilder(module, type.inputs, type.outputs);
   }
 
-  /// Add a local variable to the function.
-  ir.Local addLocal(ir.ValueType type) =>
-      body.addLocal(type, isParameter: false);
-
   @override
   ir.DefinedFunction forceBuild() =>
       ir.DefinedFunction(body.build(), finalizableIndex, type, functionName);
diff --git a/pkg/wasm_builder/lib/src/builder/global.dart b/pkg/wasm_builder/lib/src/builder/global.dart
index b7b8749..d5d7f90 100644
--- a/pkg/wasm_builder/lib/src/builder/global.dart
+++ b/pkg/wasm_builder/lib/src/builder/global.dart
@@ -10,7 +10,7 @@
 
   GlobalBuilder(ModuleBuilder module, super.index, super.type,
       [super.globalName])
-      : initializer = InstructionsBuilder(module, [type.type]);
+      : initializer = InstructionsBuilder(module, [], [type.type]);
 
   @override
   ir.DefinedGlobal forceBuild() =>
diff --git a/pkg/wasm_builder/lib/src/builder/instructions.dart b/pkg/wasm_builder/lib/src/builder/instructions.dart
index ba4cd00..9b34531 100644
--- a/pkg/wasm_builder/lib/src/builder/instructions.dart
+++ b/pkg/wasm_builder/lib/src/builder/instructions.dart
@@ -147,10 +147,14 @@
   final Map<ir.Instruction, StackTrace>? _stackTraces;
 
   /// Create a new instruction sequence.
-  InstructionsBuilder(this.module, List<ir.ValueType> outputs)
+  InstructionsBuilder(
+      this.module, List<ir.ValueType> inputs, List<ir.ValueType> outputs)
       : _stackTraces = module.watchPoints.isNotEmpty ? {} : null,
         _sourceMappings = module.sourceMapUrl == null ? null : [] {
     _labelStack.add(Expression(const [], outputs));
+    for (ir.ValueType paramType in inputs) {
+      _addParameter(paramType);
+    }
   }
 
   /// Whether the instruction sequence has been completed by the final `end`.
@@ -173,10 +177,17 @@
     }
   }
 
-  ir.Local addLocal(ir.ValueType type, {required bool isParameter}) {
+  ir.Local _addParameter(ir.ValueType type) {
     final local = ir.Local(locals.length, type);
     locals.add(local);
-    _localInitialized.add(isParameter || type.defaultable);
+    _localInitialized.add(true);
+    return local;
+  }
+
+  ir.Local addLocal(ir.ValueType type) {
+    final local = ir.Local(locals.length, type);
+    locals.add(local);
+    _localInitialized.add(type.defaultable);
     return local;
   }