diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bb933b77117..adbe103a205f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ but `false` on Windows. Use `FileSystemEntity.typeSync()` instead to get portable behavior. +#### `dart:js_interop` + +- Added a constructor to `JSSymbol`, as well as `JSSymbol.key`, + `JSSymbol.description`, and static methods for all well-known ECMAScript + symbols. + #### `dart:js_util` - dart2wasm no longer supports `dart:js_util`. Any code that imports diff --git a/pkg/dart2wasm/test/ir_tests/interop.bool.wat b/pkg/dart2wasm/test/ir_tests/interop.bool.wat index 516d1f2f03de..f0190ac43090 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.bool.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.bool.wat @@ -1,10 +1,10 @@ (module $module0 (type $#Top (struct (field $field0 i32))) - (func $"dart2wasm._149 (import)" (import "dart2wasm" "_149") (param externref) (result i32)) - (func $"dart2wasm._150 (import)" (import "dart2wasm" "_150") (param i32) (result externref)) - (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._167 (import)" (import "dart2wasm" "_167") (param externref) (result i32)) + (func $"dart2wasm._168 (import)" (import "dart2wasm" "_168") (param i32) (result externref)) + (func $"dart2wasm._292 (import)" (import "dart2wasm" "_292") (param externref) (result externref)) + (func $"dart2wasm._293 (import)" (import "dart2wasm" "_293") (param externref) (result externref)) (global $"C2 false" (ref $#Top) <...>) (global $"C40 true" (ref $#Top) <...>) (global $"boolValueNullable initialized" (mut i32) <...>) @@ -17,8 +17,8 @@ (func $"testBoolConstant " (local $var0 externref) i32.const 1 - call $"dart2wasm._150 (import)" - call $"dart2wasm._274 (import)" + call $"dart2wasm._168 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result i32) @@ -26,14 +26,14 @@ unreachable else local.get $var0 - call $"dart2wasm._149 (import)" + call $"dart2wasm._167 (import)" end call $"sinkBool " ) (func $"testBoolConstantNullable " (local $var0 externref) ref.null noextern - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var0 call $isDartNull if (result (ref null $#Top)) @@ -42,7 +42,7 @@ global.get $"C40 true" global.get $"C2 false" local.get $var0 - call $"dart2wasm._149 (import)" + call $"dart2wasm._167 (import)" select (ref $#Top) end call $"sinkBoolNullable " @@ -50,8 +50,8 @@ (func $"testBoolValue " (local $var0 externref) call $"boolValue implicit getter" - call $"dart2wasm._150 (import)" - call $"dart2wasm._274 (import)" + call $"dart2wasm._168 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result i32) @@ -59,7 +59,7 @@ unreachable else local.get $var0 - call $"dart2wasm._149 (import)" + call $"dart2wasm._167 (import)" end call $"sinkBool " ) @@ -91,7 +91,7 @@ local.get $var0 call $jsifyRaw end - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var1 call $isDartNull if (result (ref null $#Top)) @@ -100,7 +100,7 @@ global.get $"C40 true" global.get $"C2 false" local.get $var1 - call $"dart2wasm._149 (import)" + call $"dart2wasm._167 (import)" select (ref $#Top) end call $"sinkBoolNullable " diff --git a/pkg/dart2wasm/test/ir_tests/interop.double.wat b/pkg/dart2wasm/test/ir_tests/interop.double.wat index a3e505271f0b..cf6f62bb3ff9 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.double.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.double.wat @@ -14,10 +14,10 @@ (type $_Type (sub $#Top (struct (field $field0 i32) (field $isDeclaredNullable i32)))) - (func $"dart2wasm._147 (import)" (import "dart2wasm" "_147") (param externref) (result f64)) - (func $"dart2wasm._148 (import)" (import "dart2wasm" "_148") (param f64) (result externref)) - (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param f64) (result externref)) - (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._165 (import)" (import "dart2wasm" "_165") (param externref) (result f64)) + (func $"dart2wasm._166 (import)" (import "dart2wasm" "_166") (param f64) (result externref)) + (func $"dart2wasm._292 (import)" (import "dart2wasm" "_292") (param f64) (result externref)) + (func $"dart2wasm._293 (import)" (import "dart2wasm" "_293") (param externref) (result externref)) (global $"C319 _TopType" (ref $_TopType) <...>) (global $"C66 WasmArray>[729]" (ref $Array>) <...>) (global $"doubleValueNullable initialized" (mut i32) <...>) @@ -30,7 +30,7 @@ (func $"testDoubleConstant " (local $var0 externref) f64.const 1.1 - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result f64) @@ -38,14 +38,14 @@ unreachable else local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" end call $"sinkDouble " ) (func $"testDoubleConstantNullable " (local $var0 externref) ref.null noextern - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var0 call $isDartNull if (result (ref null $BoxedDouble)) @@ -53,7 +53,7 @@ else i32.const 84 local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" struct.new $BoxedDouble end call $"sinkDoubleNullable " @@ -61,7 +61,7 @@ (func $"testDoubleValue " (local $var0 externref) call $"doubleValue implicit getter" - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result f64) @@ -69,7 +69,7 @@ unreachable else local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" end call $"sinkDouble " ) @@ -121,7 +121,7 @@ if local.get $var1 struct.get $BoxedDouble $value - call $"dart2wasm._148 (import)" + call $"dart2wasm._166 (import)" br $label0 end local.get $var1 @@ -177,7 +177,7 @@ unreachable end $label0 end - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var5 call $isDartNull if (result (ref null $BoxedDouble)) @@ -185,7 +185,7 @@ else i32.const 84 local.get $var5 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" struct.new $BoxedDouble end call $"sinkDoubleNullable " diff --git a/pkg/dart2wasm/test/ir_tests/interop.int.wat b/pkg/dart2wasm/test/ir_tests/interop.int.wat index 0612cfacf9da..1169c34adc1b 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.int.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.int.wat @@ -4,8 +4,8 @@ (type $BoxedInt (sub final $#Top (struct (field $field0 i32) (field $value i64)))) - (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._292 (import)" (import "dart2wasm" "_292") (param externref) (result externref)) + (func $"dart2wasm._293 (import)" (import "dart2wasm" "_293") (param externref) (result externref)) (global $"intValueNullable initialized" (mut i32) <...>) (global $intValueNullable (mut (ref null $BoxedInt)) <...>) (func $_throwArgumentNullError <...>) @@ -17,7 +17,7 @@ (local $var0 externref) i64.const 1 call $jsifyInt - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result i64) @@ -32,7 +32,7 @@ (func $"testIntConstantNullable " (local $var0 externref) ref.null noextern - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var0 call $isDartNull if (result (ref null $BoxedInt)) @@ -49,7 +49,7 @@ (local $var0 externref) call $"intValue implicit getter" call $jsifyInt - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result i64) @@ -88,7 +88,7 @@ local.get $var0 call $jsifyRaw end - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var1 call $isDartNull if (result (ref null $BoxedInt)) diff --git a/pkg/dart2wasm/test/ir_tests/interop.num.wat b/pkg/dart2wasm/test/ir_tests/interop.num.wat index 37bc465f4dea..0883baccef3a 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.num.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.num.wat @@ -14,10 +14,10 @@ (type $_Type (sub $#Top (struct (field $field0 i32) (field $isDeclaredNullable i32)))) - (func $"dart2wasm._147 (import)" (import "dart2wasm" "_147") (param externref) (result f64)) - (func $"dart2wasm._148 (import)" (import "dart2wasm" "_148") (param f64) (result externref)) - (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._165 (import)" (import "dart2wasm" "_165") (param externref) (result f64)) + (func $"dart2wasm._166 (import)" (import "dart2wasm" "_166") (param f64) (result externref)) + (func $"dart2wasm._292 (import)" (import "dart2wasm" "_292") (param externref) (result externref)) + (func $"dart2wasm._293 (import)" (import "dart2wasm" "_293") (param externref) (result externref)) (table $dtable0 745 funcref) (global $"C319 _TopType" (ref $_TopType) <...>) (global $"C66 WasmArray>[729]" (ref $Array>) <...>) @@ -33,7 +33,7 @@ (local $var0 externref) i64.const 1 call $jsifyInt - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result f64) @@ -41,15 +41,15 @@ unreachable else local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" end call $"sinkNum " ) (func $"testNumConstantDouble " (local $var0 externref) f64.const 1.1 - call $"dart2wasm._148 (import)" - call $"dart2wasm._274 (import)" + call $"dart2wasm._166 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result f64) @@ -57,14 +57,14 @@ unreachable else local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" end call $"sinkNum " ) (func $"testNumConstantNullable " (local $var0 externref) ref.null noextern - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var0 call $isDartNull if (result (ref null $BoxedDouble)) @@ -72,7 +72,7 @@ else i32.const 84 local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" struct.new $BoxedDouble end call $"sinkNumNullable " @@ -81,7 +81,7 @@ (local $var0 externref) call $"numValue implicit getter" call $jsifyNum - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result f64) @@ -89,7 +89,7 @@ unreachable else local.get $var0 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" end call $"sinkNum " ) @@ -253,7 +253,7 @@ ref.null noextern end $label0 end - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var5 call $isDartNull if (result (ref null $BoxedDouble)) @@ -261,7 +261,7 @@ else i32.const 84 local.get $var5 - call $"dart2wasm._147 (import)" + call $"dart2wasm._165 (import)" struct.new $BoxedDouble end call $"sinkNumNullable " diff --git a/pkg/dart2wasm/test/ir_tests/interop.string.wat b/pkg/dart2wasm/test/ir_tests/interop.string.wat index bcb0497d96fc..90ab54a386e5 100644 --- a/pkg/dart2wasm/test/ir_tests/interop.string.wat +++ b/pkg/dart2wasm/test/ir_tests/interop.string.wat @@ -4,8 +4,8 @@ (type $JSStringImpl (sub final $#Top (struct (field $field0 i32) (field $_ref externref)))) - (func $"dart2wasm._274 (import)" (import "dart2wasm" "_274") (param externref) (result externref)) - (func $"dart2wasm._275 (import)" (import "dart2wasm" "_275") (param externref) (result externref)) + (func $"dart2wasm._292 (import)" (import "dart2wasm" "_292") (param externref) (result externref)) + (func $"dart2wasm._293 (import)" (import "dart2wasm" "_293") (param externref) (result externref)) (global $.a (import "" "a") (ref extern)) (global $"stringValueNullable initialized" (mut i32) <...>) (global $stringValueNullable (mut (ref null $JSStringImpl)) <...>) @@ -18,7 +18,7 @@ (func $"testStringConstant " (local $var0 externref) global.get $.a - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result (ref $JSStringImpl)) @@ -33,7 +33,7 @@ (func $"testStringConstantNullable " (local $var0 externref) ref.null noextern - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var0 call $isDartNull if (result (ref null $JSStringImpl)) @@ -48,7 +48,7 @@ (local $var0 externref) call $"stringValue implicit getter" struct.get $JSStringImpl $_ref - call $"dart2wasm._274 (import)" + call $"dart2wasm._292 (import)" local.tee $var0 call $isDartNull if (result (ref $JSStringImpl)) @@ -85,7 +85,7 @@ local.get $var0 call $jsifyRaw end - call $"dart2wasm._275 (import)" + call $"dart2wasm._293 (import)" local.tee $var1 call $isDartNull if (result (ref null $JSStringImpl)) diff --git a/sdk/lib/js_interop/js_interop.dart b/sdk/lib/js_interop/js_interop.dart index 9b99d004c51e..5ac9a595b509 100644 --- a/sdk/lib/js_interop/js_interop.dart +++ b/sdk/lib/js_interop/js_interop.dart @@ -529,8 +529,128 @@ extension type JSBoolean._(JSBooleanRepType _jsBoolean) implements JSAny {} /// A JavaScript string. extension type JSString._(JSStringRepType _jsString) implements JSAny {} +@JS('Symbol') +external JSSymbol _constructSymbol([String? description]); + /// A JavaScript `Symbol`. -extension type JSSymbol._(JSSymbolRepType _jsSymbol) implements JSAny {} +@JS('Symbol') +extension type JSSymbol._(JSSymbolRepType _jsSymbol) implements JSAny { + // TODO(srujzs): See if this can be made `const` so it can be used in similar + // situations to a Dart symbol literal. + /// Creates a new, unique JavaScript `Symbol`. + /// + /// If [description] is provided, it's used for debugging but not to access + /// the symbol itself. + @Since('3.11') + JSSymbol([String? description]) + : _jsSymbol = + (description == null + ? _constructSymbol() + : _constructSymbol(description)) + ._jsSymbol; + + /// Searches for an existing symbol in a runtime-wide symbol registry with the + /// given key and returns it if found. + /// + /// Otherwise, creates a new symbol with this key, adds it to the global + /// registry, and returns it. + @Since('3.11') + @JS('for') + external static JSSymbol forKey(String key); + + /// See [`Symbol.asyncIterator`]. + /// + /// [`Symbol.asyncIterator`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator + @Since('3.11') + external static JSSymbol get asyncIterator; + + /// See [`Symbol.hasInstance`]. + /// + /// [`Symbol.hasInstance`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance + @Since('3.11') + external static JSSymbol get hasInstance; + + /// See [`Symbol.isConcatSpreadable`]. + /// + /// [`Symbol.isConcatSpreadable`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/isConcatSpreadable + @Since('3.11') + external static JSSymbol get isConcatSpreadable; + + /// See [`Symbol.iterator`]. + /// + /// [`Symbol.iterator`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator + @Since('3.11') + external static JSSymbol get iterator; + + /// See [`Symbol.match`]. + /// + /// [`Symbol.match`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/match + @Since('3.11') + external static JSSymbol get match; + + /// See [`Symbol.matchAll`]. + /// + /// [`Symbol.matchAll`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/matchAll + @Since('3.11') + external static JSSymbol get matchAll; + + /// See [`Symbol.replace`]. + /// + /// [`Symbol.replace`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/replace + @Since('3.11') + external static JSSymbol get replace; + + /// See [`Symbol.search`]. + /// + /// [`Symbol.search`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/search + @Since('3.11') + external static JSSymbol get search; + + /// See [`Symbol.species`]. + /// + /// [`Symbol.species`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/species + @Since('3.11') + external static JSSymbol get species; + + /// See [`Symbol.split`]. + /// + /// [`Symbol.split`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/split + @Since('3.11') + external static JSSymbol get split; + + /// See [`Symbol.toPrimitive`]. + /// + /// [`Symbol.toPrimitive`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive + @Since('3.11') + external static JSSymbol get toPrimitive; + + /// See [`Symbol.toStringTag`]. + /// + /// [`Symbol.toStringTag`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag + @Since('3.11') + external static JSSymbol get toStringTag; + + /// See [`Symbol.unscopables`]. + /// + /// [`Symbol.unscopables`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables + @Since('3.11') + external static JSSymbol get unscopables; + + @Since('3.11') + @JS('keyFor') + external static String? _keyFor(JSSymbol symbol); + + /// Returns the shared symbol key from the global symbol registry for this + /// symbol (as registered with [forKey]), if this symbol was created with + /// [JSSymbol.forKey]. + @Since('3.11') + String? get key => _keyFor(this); + + /// A string containing the description of the symbol, as passed to + /// [JSSymbol.new]. + @Since('3.11') + external String get description; +} /// A JavaScript `BigInt`. extension type JSBigInt._(JSBigIntRepType _jsBigInt) implements JSAny {} 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 64700c7d8da7..fc832a7a85bb 100644 --- a/tests/lib/js/static_interop_test/js_types_test.dart +++ b/tests/lib/js/static_interop_test/js_types_test.dart @@ -128,9 +128,6 @@ external JSString str; @JS() external JSSymbol symbol; -@JS('Symbol') -external JSSymbol createSymbol(String value); - extension on JSSymbol { @JS('toString') external String toStringExternal(); @@ -536,10 +533,27 @@ void syncTests() { Expect.equals('foo', dartStr); // [JSSymbol] - symbol = createSymbol('foo'); + symbol = JSSymbol('foo'); Expect.isTrue(symbol is JSSymbol); Expect.isTrue(confuse(symbol) is JSSymbol); Expect.equals('Symbol(foo)', symbol.toStringExternal()); + Expect.equals(symbol.description, 'foo'); + Expect.notEquals(JSSymbol.forKey('foo'), symbol); + Expect.equals(JSSymbol.forKey('foo'), JSSymbol.forKey('foo')); + Expect.equals(JSSymbol.forKey('foo').key, 'foo'); + Expect.isTrue(JSSymbol.asyncIterator is JSSymbol); + Expect.isTrue(JSSymbol.hasInstance is JSSymbol); + Expect.isTrue(JSSymbol.isConcatSpreadable is JSSymbol); + Expect.isTrue(JSSymbol.iterator is JSSymbol); + Expect.isTrue(JSSymbol.match is JSSymbol); + Expect.isTrue(JSSymbol.matchAll is JSSymbol); + Expect.isTrue(JSSymbol.replace is JSSymbol); + Expect.isTrue(JSSymbol.search is JSSymbol); + Expect.isTrue(JSSymbol.species is JSSymbol); + Expect.isTrue(JSSymbol.split is JSSymbol); + Expect.isTrue(JSSymbol.toPrimitive is JSSymbol); + Expect.isTrue(JSSymbol.toStringTag is JSSymbol); + Expect.isTrue(JSSymbol.unscopables is JSSymbol); // [JSBigInt] bigInt = createBigInt('9876543210000000000000123456789');