[dart2wasm] Make dynamic call forwarders use normal CallTarget infrastructure

So far running the compiler with `--print-wasm` wouldn't print
the code for dynamic call forwarders as it doesn't use the same
compiler infrastructure as normal function compilations.

This CL makes the dynamic call forwarders be `CallTarget`s that
can be called and if so will enqueue a `CompilationTask` in the
compilation queue.

This will ensure we treat those forwarders as any other target
we may call, which will also make e.g. `--print-wasm` work.

Change-Id: Ieb5673befa1456e941276d538e2212ff8d4077fc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428700
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 5d11477..158c669 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -1754,9 +1754,10 @@
     final typeArguments = node.arguments.types;
     final positionalArguments = node.arguments.positional;
     final namedArguments = node.arguments.named;
+    final memberName = node.name.text;
     final forwarder = translator
         .getDynamicForwardersForModule(b.module)
-        .getDynamicInvocationForwarder(node.name.text);
+        .getDynamicInvocationForwarder(memberName);
 
     // Evaluate receiver
     translateExpression(receiver, translator.topInfo.nullableType);
@@ -1816,7 +1817,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);
-    createInvocationObject(translator, b, forwarder.memberName, typeArgsLocal,
+    createInvocationObject(translator, b, memberName, typeArgsLocal,
         positionalArgsLocal, namedArgsLocal);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
@@ -2173,9 +2174,10 @@
   @override
   w.ValueType visitDynamicGet(DynamicGet node, w.ValueType expectedType) {
     final receiver = node.receiver;
+    final memberName = node.name.text;
     final forwarder = translator
         .getDynamicForwardersForModule(b.module)
-        .getDynamicGetForwarder(node.name.text);
+        .getDynamicGetForwarder(memberName);
 
     // Evaluate receiver
     translateExpression(receiver, translator.topInfo.nullableType);
@@ -2189,7 +2191,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, b, forwarder.memberName);
+    createGetterInvocationObject(translator, b, memberName);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
     b.unreachable();
@@ -2205,9 +2207,10 @@
   w.ValueType visitDynamicSet(DynamicSet node, w.ValueType expectedType) {
     final receiver = node.receiver;
     final value = node.value;
+    final memberName = node.name.text;
     final forwarder = translator
         .getDynamicForwardersForModule(b.module)
-        .getDynamicSetForwarder(node.name.text);
+        .getDynamicSetForwarder(memberName);
 
     // Evaluate receiver
     translateExpression(receiver, translator.topInfo.nullableType);
@@ -2226,8 +2229,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);
-    createSetterInvocationObject(
-        translator, b, forwarder.memberName, positionalArgLocal);
+    createSetterInvocationObject(translator, b, memberName, positionalArgLocal);
 
     call(translator.noSuchMethodErrorThrowWithInvocation.reference);
     b.unreachable();
diff --git a/pkg/dart2wasm/lib/dynamic_forwarders.dart b/pkg/dart2wasm/lib/dynamic_forwarders.dart
index 03237d1..045ba19 100644
--- a/pkg/dart2wasm/lib/dynamic_forwarders.dart
+++ b/pkg/dart2wasm/lib/dynamic_forwarders.dart
@@ -6,7 +6,7 @@
 import 'package:wasm_builder/wasm_builder.dart' as w;
 
 import 'class_info.dart';
-import 'code_generator.dart' show MacroAssembler;
+import 'code_generator.dart' show CallTarget, CodeGenerator, MacroAssembler;
 import 'dispatch_table.dart';
 import 'reference_extensions.dart';
 import 'translator.dart';
@@ -18,36 +18,61 @@
   final Translator translator;
   final w.ModuleBuilder callingModule;
 
-  final Map<String, Forwarder> _getterForwarderOfName = {};
-  final Map<String, Forwarder> _setterForwarderOfName = {};
-  final Map<String, Forwarder> _methodForwarderOfName = {};
+  final Map<String, CallTarget> _getterForwarderOfName = {};
+  final Map<String, CallTarget> _setterForwarderOfName = {};
+  final Map<String, CallTarget> _methodForwarderOfName = {};
 
   DynamicForwarders(this.translator, this.callingModule);
 
-  Forwarder getDynamicGetForwarder(String memberName) =>
-      _getterForwarderOfName[memberName] ??= Forwarder._(
-          translator, _ForwarderKind.Getter, memberName, callingModule)
-        .._generateCode(translator);
+  CallTarget getDynamicGetForwarder(String memberName) =>
+      _getterForwarderOfName[memberName] ??= _DynamicForwarderCallTarget(
+          translator, _ForwarderKind.Getter, memberName, callingModule);
 
-  Forwarder getDynamicSetForwarder(String memberName) =>
-      _setterForwarderOfName[memberName] ??= Forwarder._(
-          translator, _ForwarderKind.Setter, memberName, callingModule)
-        .._generateCode(translator);
+  CallTarget getDynamicSetForwarder(String memberName) =>
+      _setterForwarderOfName[memberName] ??= _DynamicForwarderCallTarget(
+          translator, _ForwarderKind.Setter, memberName, callingModule);
 
-  Forwarder getDynamicInvocationForwarder(String memberName) {
+  CallTarget getDynamicInvocationForwarder(String memberName) {
     // Add Wasm function to the map before generating the forwarder code, to
     // allow recursive calls in the "call" forwarder.
     var forwarder = _methodForwarderOfName[memberName];
     if (forwarder == null) {
-      forwarder = Forwarder._(
+      forwarder = _DynamicForwarderCallTarget(
           translator, _ForwarderKind.Method, memberName, callingModule);
       _methodForwarderOfName[memberName] = forwarder;
-      forwarder._generateCode(translator);
     }
     return forwarder;
   }
 }
 
+class _DynamicForwarderCallTarget extends CallTarget {
+  final Translator translator;
+  final _ForwarderKind _kind;
+  final String memberName;
+  final w.ModuleBuilder callingModule;
+
+  _DynamicForwarderCallTarget(
+      this.translator, this._kind, this.memberName, this.callingModule)
+      : assert(!translator.isDynamicSubmodule ||
+            (memberName == 'call' && _kind == _ForwarderKind.Getter)),
+        super(_kind.functionType(translator));
+
+  @override
+  String get name => 'Dynamic $_kind forwarder for "$memberName"';
+
+  @override
+  bool get supportsInlining => false;
+
+  @override
+  late final w.BaseFunction function = (() {
+    final function = callingModule.functions.define(signature, name);
+    final forwarder =
+        _DynamicForwarderCodeGenerator(translator, _kind, memberName, function);
+    translator.compilationQueue.add(CompilationTask(function, forwarder));
+    return function;
+  })();
+}
+
 /// A function that "forwards" a dynamic get, set, or invocation to the right
 /// type checking member.
 ///
@@ -67,21 +92,19 @@
 /// A forwarder calls `noSuchMethod` on the receiver when a matching member is
 /// not found, or the passed arguments do not match the expected parameters of
 /// the member.
-class Forwarder {
+class _DynamicForwarderCodeGenerator extends CodeGenerator {
+  final Translator translator;
   final _ForwarderKind _kind;
-
   final String memberName;
-
   final w.FunctionBuilder function;
 
-  Forwarder._(Translator translator, this._kind, this.memberName,
-      w.ModuleBuilder module)
-      : function = module.functions.define(_kind.functionType(translator),
-            "$_kind forwarder for '$memberName'"),
-        assert(!translator.isDynamicSubmodule ||
-            (memberName == 'call' && _kind == _ForwarderKind.Getter));
+  _DynamicForwarderCodeGenerator(
+      this.translator, this._kind, this.memberName, this.function);
 
-  void _generateCode(Translator translator) {
+  @override
+  void generate(w.InstructionsBuilder b, List<w.Local> paramLocals,
+      w.Label? returnLabel) {
+    assert(returnLabel == null); // no inlining support atm.
     switch (_kind) {
       case _ForwarderKind.Getter:
         _generateGetterCode(translator);