From a6e875988869be0998c0c600d9cc544ec910dee0 Mon Sep 17 00:00:00 2001 From: Srujan Gaddam Date: Tue, 23 Jan 2024 23:39:49 +0000 Subject: [PATCH] [dart2wasm] Add concrete box and symbol for JSBoxedDartObject The current implementation externalizes and internalizes the Dart value instead of adding a box and using a runtime-specific symbol. This makes the implementation consistent with the JS backends. Change-Id: Iefa382f742bc819b18dfe27ca33741b12473ee39 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347222 Commit-Queue: Srujan Gaddam Reviewed-by: Sigmund Cherem --- CHANGELOG.md | 9 ++++++++ .../_internal/wasm/lib/js_interop_patch.dart | 23 +++++++++++++++++-- .../js/static_interop_test/js_types_test.dart | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0369f54ac8c..669a1531fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ [pub-security-advisories]: https://dart.dev/go/pub-security-advisories +### Libraries + +#### `dart:js_interop` + +- On dart2wasm, `JSBoxedDartObject` now is an actual JS object that wraps the + opaque Dart value instead of only externalizing the value. Like the JS + backends, you'll now get a more useful error when trying to use it in another + Dart runtime. + ## 3.3.0 ### Language diff --git a/sdk/lib/_internal/wasm/lib/js_interop_patch.dart b/sdk/lib/_internal/wasm/lib/js_interop_patch.dart index aef57c95003..8f9c2926d8a 100644 --- a/sdk/lib/_internal/wasm/lib/js_interop_patch.dart +++ b/sdk/lib/_internal/wasm/lib/js_interop_patch.dart @@ -94,11 +94,27 @@ extension FunctionToJSExportedDartFunction on Function { 'by the interop transformer.'); } +/// Embedded global property for wrapped Dart objects passed via JS interop. +/// +/// This is a Symbol so that different Dart applications don't share Dart +/// objects from different Dart runtimes. We expect all [JSBoxedDartObject]s to +/// have this Symbol. +final JSSymbol _jsBoxedDartObjectProperty = _boxNonNullable( + js_helper.JS('() => Symbol("jsBoxedDartObjectProperty")')); + /// [JSBoxedDartObject] <-> [Object] @patch extension JSBoxedDartObjectToObject on JSBoxedDartObject { @patch - Object get toDart => jsObjectToDartObject(toExternRef); + Object get toDart { + final val = js_helper.JS('(o,s) => o[s]', this.toExternRef, + _jsBoxedDartObjectProperty.toExternRef); + if (isDartNull(val)) { + throw 'Expected a wrapped Dart object, but got a JS object or a wrapped ' + 'Dart object from a separate runtime instead.'; + } + return jsObjectToDartObject(val); + } } @patch @@ -108,7 +124,10 @@ extension ObjectToJSBoxedDartObject on Object { if (this is JSValue) { throw 'Attempting to box non-Dart object.'; } - return _boxNonNullable(jsObjectFromDartObject(this)); + final box = JSObject(); + js_helper.JS('(o,s,v) => o[s] = v', box.toExternRef, + _jsBoxedDartObjectProperty.toExternRef, jsObjectFromDartObject(this)); + return box as JSBoxedDartObject; } } diff --git a/tests/lib/js/static_interop_test/js_types_test.dart b/tests/lib/js/static_interop_test/js_types_test.dart index e6da910af27..666d3855bcd 100644 --- a/tests/lib/js/static_interop_test/js_types_test.dart +++ b/tests/lib/js/static_interop_test/js_types_test.dart @@ -173,6 +173,7 @@ void syncTests() { expect(edo is JSBoxedDartObject, true); expect(confuse(edo) is JSBoxedDartObject, true); expect(((edo as JSBoxedDartObject).toDart as DartObject).foo, 'bar'); + expect(edo.instanceOfString('Object'), true); // Functions should be boxed without assertInterop. final concat = (String a, String b) => a + b; edo = concat.toJSBox;