[dart2wasm] Convert to/from externref via Wasm instructions

This adds support for the newly added `extern.externalize` and
`extern.internalize` instructions and uses these for converting to/from
`externref` instead of a JS round-trip.

Change-Id: If18d8f44ddf013d4c26bf1597be91bcd0db41c5a
Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255462
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index 098c19f..fbc75b9 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -1007,6 +1007,29 @@
       }
     }
 
+    // dart:wasm static functions
+    if (node.target.enclosingLibrary.name == "dart.wasm") {
+      Expression value = node.arguments.positional.single;
+      switch (name) {
+        case "_externalizeNonNullable":
+          codeGen.wrap(value, w.RefType.any(nullable: false));
+          b.extern_externalize();
+          return w.RefType.extern(nullable: false);
+        case "_externalizeNullable":
+          codeGen.wrap(value, w.RefType.any(nullable: true));
+          b.extern_externalize();
+          return w.RefType.extern(nullable: true);
+        case "_internalizeNonNullable":
+          codeGen.wrap(value, w.RefType.extern(nullable: false));
+          b.extern_internalize();
+          return w.RefType.any(nullable: false);
+        case "_internalizeNullable":
+          codeGen.wrap(value, w.RefType.extern(nullable: true));
+          b.extern_internalize();
+          return w.RefType.any(nullable: true);
+      }
+    }
+
     return null;
   }
 
diff --git a/pkg/wasm_builder/lib/src/instructions.dart b/pkg/wasm_builder/lib/src/instructions.dart
index 69c210b..639657a 100644
--- a/pkg/wasm_builder/lib/src/instructions.dart
+++ b/pkg/wasm_builder/lib/src/instructions.dart
@@ -1251,6 +1251,22 @@
     _writeLabel(label);
   }
 
+  /// Emit an `extern.internalize` instruction.
+  void extern_internalize() {
+    assert(_verifyTypesFun(const [RefType.extern(nullable: true)],
+        (inputs) => [RefType.any(nullable: inputs.single.nullable)],
+        trace: ['extern.internalize']));
+    writeBytes(const [0xFB, 0x70]);
+  }
+
+  /// Emit an `extern.externalize` instruction.
+  void extern_externalize() {
+    assert(_verifyTypesFun(const [RefType.any(nullable: true)],
+        (inputs) => [RefType.extern(nullable: inputs.single.nullable)],
+        trace: ['extern.externalize']));
+    writeBytes(const [0xFB, 0x71]);
+  }
+
   // Numeric instructions
 
   /// Emit an `i32.const` instruction.
diff --git a/sdk/lib/wasm/wasm_types.dart b/sdk/lib/wasm/wasm_types.dart
index cda6ba4..b95e26d 100644
--- a/sdk/lib/wasm/wasm_types.dart
+++ b/sdk/lib/wasm/wasm_types.dart
@@ -52,15 +52,9 @@
   WasmAnyRef? internalize() => _internalizeNullable(this);
 }
 
-// TODO(askesc): Add intrinsics for these when the `extern.externalize` and
-// `extern.internalize` instructions are implemented in V8.
-@pragma("wasm:import", "dart2wasm.roundtrip")
 external WasmExternRef _externalizeNonNullable(WasmAnyRef ref);
-@pragma("wasm:import", "dart2wasm.roundtrip")
 external WasmExternRef? _externalizeNullable(WasmAnyRef? ref);
-@pragma("wasm:import", "dart2wasm.roundtrip")
 external WasmAnyRef _internalizeNonNullable(WasmExternRef ref);
-@pragma("wasm:import", "dart2wasm.roundtrip")
 external WasmAnyRef? _internalizeNullable(WasmExternRef? ref);
 
 /// The Wasm `funcref` type.