From 3fd96e2cb8ee5c5fed7655fa69279148cdea9403 Mon Sep 17 00:00:00 2001 From: Joshua Litt Date: Wed, 8 Jun 2022 20:38:52 +0000 Subject: [PATCH] [dart2wasm] Wire up stubbed out dart:js on `dart2wasm` Change-Id: Ia06db4b1b2f3d07278819936842a4185cc9a45f8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247329 Reviewed-by: Srujan Gaddam Commit-Queue: Joshua Litt --- .../lib/src/js_interop.dart | 4 +- .../js_util_wasm_optimizer.dart | 12 +- pkg/dart2wasm/lib/target.dart | 8 +- sdk/lib/_internal/wasm/lib/js_helper.dart | 28 +++- sdk/lib/_internal/wasm/lib/js_patch.dart | 120 ++++++++++++++++++ .../wasm/lib/js_util_wasm_patch.dart | 21 --- sdk/lib/js_util/js_util_sources.gni | 5 +- sdk/lib/libraries.json | 11 +- sdk/lib/libraries.yaml | 8 +- tests/web/wasm/callback_test.dart | 6 +- tests/web/wasm/js_util_test.dart | 2 +- tests/web/wasm/static_interop_test.dart | 4 +- 12 files changed, 170 insertions(+), 59 deletions(-) create mode 100644 sdk/lib/_internal/wasm/lib/js_patch.dart delete mode 100644 sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart diff --git a/pkg/_js_interop_checks/lib/src/js_interop.dart b/pkg/_js_interop_checks/lib/src/js_interop.dart index 3f3c517c32c..29e852c44e5 100644 --- a/pkg/_js_interop_checks/lib/src/js_interop.dart +++ b/pkg/_js_interop_checks/lib/src/js_interop.dart @@ -60,12 +60,10 @@ List getNativeNames(Annotatable a) { } final _packageJs = Uri.parse('package:js/js.dart'); -final _jsWasm = Uri.parse('dart:js_wasm'); final _internalJs = Uri.parse('dart:_js_annotations'); final _jsHelper = Uri.parse('dart:_js_helper'); -bool _isAnyInteropUri(Uri uri) => - uri == _packageJs || uri == _internalJs || uri == _jsWasm; +bool _isAnyInteropUri(Uri uri) => uri == _packageJs || uri == _internalJs; /// Returns true if [value] is the interop annotation whose class is /// [annotationClassName] from `package:js` or from `dart:_js_annotations`. diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart index a61f47f9a18..5abcdc223b6 100644 --- a/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart +++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_wasm_optimizer.dart @@ -78,11 +78,11 @@ class JsUtilWasmOptimizer extends Transformer { _jsifyRawTarget = _coreTypes.index .getTopLevelProcedure('dart:_js_helper', 'jsifyRaw'), _wrapDartCallbackTarget = _coreTypes.index - .getTopLevelProcedure('dart:js_util_wasm', '_wrapDartCallback'), + .getTopLevelProcedure('dart:_js_helper', '_wrapDartCallback'), _newObjectTarget = _coreTypes.index.getTopLevelProcedure('dart:js_util', 'newObject'), - _allowInteropTarget = _coreTypes.index - .getTopLevelProcedure('dart:js_util_wasm', 'allowInterop'), + _allowInteropTarget = + _coreTypes.index.getTopLevelProcedure('dart:js', 'allowInterop'), _wasmAnyRefClass = _coreTypes.index.getClass('dart:wasm', 'WasmAnyRef'), _objectClass = _coreTypes.objectClass, _pragmaClass = _coreTypes.pragmaClass, @@ -283,8 +283,10 @@ class JsUtilWasmOptimizer extends Transformer { StaticInvocation _allowInterop( Procedure node, FunctionType type, Expression argument) { String callbackTrampolineName = _createCallbackTrampoline(node, type); - return StaticInvocation(_wrapDartCallbackTarget, - Arguments([argument, StringLiteral(callbackTrampolineName)])); + return StaticInvocation( + _wrapDartCallbackTarget, + Arguments([argument, StringLiteral(callbackTrampolineName)], + types: [type])); } StaticGet get _globalThis => StaticGet(_globalThisMember); diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart index 4be43adb9fc..12b44085a2c 100644 --- a/pkg/dart2wasm/lib/target.dart +++ b/pkg/dart2wasm/lib/target.dart @@ -50,9 +50,8 @@ class WasmTarget extends Target { 'dart:_js_helper', 'dart:typed_data', 'dart:nativewrappers', + 'dart:js', 'dart:js_util', - 'dart:js_util_wasm', - 'dart:js_wasm', 'dart:wasm', 'dart:developer', ]; @@ -62,9 +61,8 @@ class WasmTarget extends Target { 'dart:_js_helper', 'dart:collection', 'dart:typed_data', + 'dart:js', 'dart:js_util', - 'dart:js_util_wasm', - 'dart:js_wasm', 'dart:wasm', ]; @@ -107,7 +105,7 @@ class WasmTarget extends Target { ChangedStructureNotifier? changedStructureNotifier}) { List? transitiveImportingJSInterop = jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed( - component, Uri.parse("dart:js_wasm")); + component, Uri.parse("package:js/js.dart")); if (transitiveImportingJSInterop == null) { logger?.call("Skipped JS interop transformations"); } else { diff --git a/sdk/lib/_internal/wasm/lib/js_helper.dart b/sdk/lib/_internal/wasm/lib/js_helper.dart index fc392e7da27..5acce01dbc8 100644 --- a/sdk/lib/_internal/wasm/lib/js_helper.dart +++ b/sdk/lib/_internal/wasm/lib/js_helper.dart @@ -87,9 +87,21 @@ external WasmAnyRef? setPropertyRaw( external WasmAnyRef? callMethodVarArgsRaw( WasmAnyRef o, WasmAnyRef method, WasmAnyRef? args); +// Currently, `allowInterop` returns a Function type. This is unfortunate for +// Dart2wasm because it means arbitrary Dart functions can flow to JS util +// calls. Our only solutions is to cache every function called with +// `allowInterop` and to replace them with the wrapped variant when they flow +// to JS. +// NOTE: We are not currently replacing functions returned from JS. +Map functionToJSWrapper = {}; + WasmAnyRef? jsifyRaw(Object? object) { if (object == null) { return null; + } else if (object is Function) { + assert(functionToJSWrapper.containsKey(object), + 'Must call `allowInterop` on functions before they flow to JS'); + return functionToJSWrapper[object]?.toAnyRef(); } else if (object is JSValue) { return object.toAnyRef(); } else if (object is String) { @@ -101,7 +113,21 @@ WasmAnyRef? jsifyRaw(Object? object) { } } -/// js_util_wasm methods used by the wasm runtime. +@pragma("wasm:import", "dart2wasm.wrapDartCallback") +external WasmAnyRef _wrapDartCallbackRaw( + WasmAnyRef callback, WasmAnyRef trampolineName); + +F _wrapDartCallback(F f, String trampolineName) { + if (functionToJSWrapper.containsKey(f)) { + return f; + } + JSValue wrappedFunction = JSValue(_wrapDartCallbackRaw( + f.toJS().toAnyRef(), trampolineName.toJS().toAnyRef())); + functionToJSWrapper[f] = wrappedFunction; + return f; +} + +/// Methods used by the wasm runtime. @pragma("wasm:export", "\$listLength") double _listLength(List list) => list.length.toDouble(); diff --git a/sdk/lib/_internal/wasm/lib/js_patch.dart b/sdk/lib/_internal/wasm/lib/js_patch.dart new file mode 100644 index 00000000000..0445321fcfe --- /dev/null +++ b/sdk/lib/_internal/wasm/lib/js_patch.dart @@ -0,0 +1,120 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// This library is only supported on dart2wasm because of [allowInterop]. + +library dart.js; + +import 'dart:_internal' show patch; +import 'dart:_js_helper'; +import 'dart:collection' show ListMixin; +import 'dart:wasm'; + +@patch +JsObject get context => throw UnimplementedError; + +@patch +class JsObject { + // No argument empty constructor to support inheritance. + JsObject._() { + throw UnimplementedError; + } + + @patch + factory JsObject(JsFunction constructor, [List? arguments]) => + throw UnimplementedError; + + @patch + factory JsObject.fromBrowserObject(Object object) => throw UnimplementedError; + + @patch + factory JsObject.jsify(Object object) => throw UnimplementedError; + + @patch + dynamic operator [](Object property) => throw UnimplementedError; + + @patch + void operator []=(Object property, Object? value) => throw UnimplementedError; + + @patch + bool operator ==(Object other) => throw UnimplementedError; + + @patch + bool hasProperty(Object property) => throw UnimplementedError; + + @patch + void deleteProperty(Object property) => throw UnimplementedError; + + @patch + bool instanceof(JsFunction type) => throw UnimplementedError; + + @patch + String toString() => throw UnimplementedError; + + @patch + dynamic callMethod(Object method, [List? args]) => throw UnimplementedError; +} + +@patch +class JsFunction extends JsObject { + @patch + factory JsFunction.withThis(Function f) => throw UnimplementedError; + + @patch + dynamic apply(List args, {thisArg}) => throw UnimplementedError; +} + +@patch +class JsArray extends JsObject with ListMixin { + @patch + factory JsArray() => throw UnimplementedError; + + @patch + factory JsArray.from(Iterable other) => throw UnimplementedError; + + @patch + E operator [](Object index) => throw UnimplementedError; + + @patch + void operator []=(Object index, E value) => throw UnimplementedError; + + @patch + int get length => throw UnimplementedError; + + @patch + void set length(int length) => throw UnimplementedError; + + @patch + void add(E value) => throw UnimplementedError; + + @patch + void addAll(Iterable iterable) => throw UnimplementedError; + + @patch + void insert(int index, E element) => throw UnimplementedError; + + @patch + E removeAt(int index) => throw UnimplementedError; + + @patch + E removeLast() => throw UnimplementedError; + + @patch + void removeRange(int start, int end) => throw UnimplementedError; + + @patch + void setRange(int start, int end, Iterable iterable, + [int skipCount = 0]) => + throw UnimplementedError; + + @patch + void sort([int compare(E a, E b)?]) => throw UnimplementedError; +} + +/// This will be lowered to a a call to `_wrapDartCallback`. +@patch +F allowInterop(F f) => throw UnimplementedError; + +@patch +Function allowInteropCaptureThis(Function f) => throw UnimplementedError; diff --git a/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart b/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart deleted file mode 100644 index 8e7e85e906b..00000000000 --- a/sdk/lib/_internal/wasm/lib/js_util_wasm_patch.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library dart.js_util_wasm; - -import "dart:_internal"; -import "dart:_js_helper"; -import "dart:wasm"; - -@patch -Object allowInterop(F f) => throw 'unreachable'; - -@pragma("wasm:import", "dart2wasm.wrapDartCallback") -external WasmAnyRef _wrapDartCallbackRaw( - WasmAnyRef callback, WasmAnyRef trampolineName); - -JSValue? _wrapDartCallback(Object callback, String trampolineName) { - return JSValue(_wrapDartCallbackRaw( - callback.toJS().toAnyRef(), trampolineName.toJS().toAnyRef())); -} diff --git a/sdk/lib/js_util/js_util_sources.gni b/sdk/lib/js_util/js_util_sources.gni index 011df2e9746..d3ac6d24d23 100644 --- a/sdk/lib/js_util/js_util_sources.gni +++ b/sdk/lib/js_util/js_util_sources.gni @@ -2,7 +2,4 @@ # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. -js_util_sdk_sources = [ - "js_util.dart", - "js_util_wasm.dart", -] +js_util_sdk_sources = [ "js_util.dart" ] diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json index 8f681646369..c47b40d40a9 100644 --- a/sdk/lib/libraries.json +++ b/sdk/lib/libraries.json @@ -235,17 +235,14 @@ "isolate": { "uri": "isolate/isolate.dart" }, + "js": { + "uri": "js/js.dart", + "patches": "_internal/wasm/lib/js_patch.dart" + }, "js_util": { "uri": "js_util/js_util.dart", "patches": "_internal/wasm/lib/js_util_patch.dart" }, - "js_util_wasm": { - "uri": "js_util/js_util_wasm.dart", - "patches": "_internal/wasm/lib/js_util_wasm_patch.dart" - }, - "js_wasm": { - "uri": "js/js_wasm.dart" - }, "math": { "uri": "math/math.dart", "patches": "_internal/wasm/lib/math_patch.dart" diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml index 97d03513fc7..aa8d1c92ef4 100644 --- a/sdk/lib/libraries.yaml +++ b/sdk/lib/libraries.yaml @@ -218,14 +218,12 @@ wasm: uri: "html/dartium/nativewrappers.dart" isolate: uri: isolate/isolate.dart + js: + uri: js/js.dart + patches: _internal/wasm/lib/js_patch.dart js_util: uri: js_util/js_util.dart patches: _internal/wasm/lib/js_util_patch.dart - js_util_wasm: - uri: js_util/js_util_wasm.dart - patches: _internal/wasm/lib/js_util_wasm_patch.dart - js_wasm: - uri: js/js_wasm.dart math: uri: math/math.dart patches: _internal/wasm/lib/math_patch.dart diff --git a/tests/web/wasm/callback_test.dart b/tests/web/wasm/callback_test.dart index dbd421f2169..2530977d3b9 100644 --- a/tests/web/wasm/callback_test.dart +++ b/tests/web/wasm/callback_test.dart @@ -3,11 +3,10 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:js_util'; -import 'dart:js_util_wasm'; -import 'dart:js_wasm'; import 'dart:wasm'; import 'package:expect/expect.dart'; +import 'package:js/js.dart'; @JS() external void eval(String code); @@ -17,8 +16,7 @@ typedef SumStringCallback = String Function(String a, String b); @JS() @staticInterop class DartFromJSCallbackHelper { - // TODO(joshualitt): Update [allowInterop] to return a function. - external factory DartFromJSCallbackHelper.factory(Object summer); + external factory DartFromJSCallbackHelper.factory(SumStringCallback summer); } extension DartFromJSCallbackHelperMethods on DartFromJSCallbackHelper { diff --git a/tests/web/wasm/js_util_test.dart b/tests/web/wasm/js_util_test.dart index 388a1eb096e..ce10c7b0369 100644 --- a/tests/web/wasm/js_util_test.dart +++ b/tests/web/wasm/js_util_test.dart @@ -3,9 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:js_util'; -import 'dart:js_wasm'; import 'package:expect/expect.dart'; +import 'package:js/js.dart'; @JS() external void eval(String code); diff --git a/tests/web/wasm/static_interop_test.dart b/tests/web/wasm/static_interop_test.dart index 7c587a028c7..c41256a4281 100644 --- a/tests/web/wasm/static_interop_test.dart +++ b/tests/web/wasm/static_interop_test.dart @@ -2,10 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:js_util_wasm'; -import 'dart:js_wasm'; - import 'package:expect/expect.dart'; +import 'package:js/js.dart'; @JS() external void eval(String code);