diff --git a/pkg/dart2wasm/lib/js/interop_specializer.dart b/pkg/dart2wasm/lib/js/interop_specializer.dart index 7acec36af828..f4250d20fc1e 100644 --- a/pkg/dart2wasm/lib/js/interop_specializer.dart +++ b/pkg/dart2wasm/lib/js/interop_specializer.dart @@ -341,19 +341,11 @@ class InteropSpecializerFactory { final MethodCollector _methodCollector; final Map> _overloadedProcedures = {}; final Map> _jsObjectLiteralMethods = {}; - late String _libraryJSString; late final ExtensionIndex _extensionIndex; InteropSpecializerFactory(this._staticTypeContext, this._util, this._methodCollector, this._extensionIndex); - void enterLibrary(Library library) { - _libraryJSString = getJSName(library); - if (_libraryJSString.isNotEmpty) { - _libraryJSString = '$_libraryJSString.'; - } - } - String _getJSString(Annotatable a, String initial) { String selectorString = getJSName(a); if (selectorString.isEmpty) { @@ -362,8 +354,13 @@ class InteropSpecializerFactory { return selectorString; } - String _getTopLevelJSString(Annotatable a, String initial) => - '$_libraryJSString${_getJSString(a, initial)}'; + String _getTopLevelJSString( + Annotatable a, String writtenName, Library enclosingLibrary) { + final name = _getJSString(a, writtenName); + final libraryName = getJSName(enclosingLibrary); + if (libraryName.isEmpty) return name; + return '$libraryName.$name'; + } /// Get the `_Specializer` for the non-constructor [node] with its /// associated [jsString] name, and the [invocation] it's used in if this is @@ -420,7 +417,8 @@ class InteropSpecializerFactory { if (node.enclosingClass != null && hasJSInteropAnnotation(node.enclosingClass!)) { final cls = node.enclosingClass!; - final clsString = _getTopLevelJSString(cls, cls.name); + final clsString = + _getTopLevelJSString(cls, cls.name, cls.enclosingLibrary); if (node.isFactory) { return _getSpecializerForConstructor( hasAnonymousAnnotation(cls), node, clsString, invocation); @@ -433,7 +431,8 @@ class InteropSpecializerFactory { final nodeDescriptor = _extensionIndex.getExtensionTypeDescriptor(node); if (nodeDescriptor != null) { final cls = _extensionIndex.getExtensionType(node)!; - final clsString = _getTopLevelJSString(cls, cls.name); + final clsString = + _getTopLevelJSString(cls, cls.name, node.enclosingLibrary); final kind = nodeDescriptor.kind; if ((kind == ExtensionTypeMemberKind.Constructor || kind == ExtensionTypeMemberKind.Factory)) { @@ -462,7 +461,9 @@ class InteropSpecializerFactory { } } else if (hasJSInteropAnnotation(node)) { return _getSpecializerForMember( - node, _getTopLevelJSString(node, node.name.text), invocation); + node, + _getTopLevelJSString(node, node.name.text, node.enclosingLibrary), + invocation); } return null; } diff --git a/pkg/dart2wasm/lib/js/interop_transformer.dart b/pkg/dart2wasm/lib/js/interop_transformer.dart index c2d321083b60..ff2b1d40da22 100644 --- a/pkg/dart2wasm/lib/js/interop_transformer.dart +++ b/pkg/dart2wasm/lib/js/interop_transformer.dart @@ -56,7 +56,6 @@ class InteropTransformer extends Transformer { @override Library visitLibrary(Library lib) { - _interopSpecializerFactory.enterLibrary(lib); _methodCollector.enterLibrary(lib); _staticTypeContext.enterLibrary(lib); lib.transformChildren(this); diff --git a/tests/lib/js/static_interop_test/extension_type/external_static_member_test.dart b/tests/lib/js/static_interop_test/extension_type/external_static_member_test.dart index 6e53886e3257..e20e9e8ddf76 100644 --- a/tests/lib/js/static_interop_test/extension_type/external_static_member_test.dart +++ b/tests/lib/js/static_interop_test/extension_type/external_static_member_test.dart @@ -2,23 +2,28 @@ // 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. +// TODO(srujzs): There's a decent amount of code duplication in this test. We +// should combine this with +// tests/lib/js/static_interop_test/external_static_member_lowerings_test.dart. + @JS() library external_static_member_test; import 'dart:js_interop'; -import 'dart:js_util' as js_util; +import 'dart:js_interop_unsafe'; import 'package:expect/minitest.dart'; +import 'external_static_member_with_namespaces.dart' as namespace; + @JS() external void eval(String code); @JS() -extension type ExternalStatic._(JSObject obj) implements Object { +extension type ExternalStatic._(JSObject obj) implements JSObject { external ExternalStatic(); external factory ExternalStatic.factory(); external ExternalStatic.multipleArgs(double a, String b); - external ExternalStatic.differentArgs(double a, [String b = '']); ExternalStatic.nonExternal() : this.obj = ExternalStatic() as JSObject; external static String field; @@ -34,39 +39,20 @@ extension type ExternalStatic._(JSObject obj) implements Object { external static set renamedGetSet(String val); external static String method(); - external static String differentArgsMethod(String a, [String b = '']); @JS('method') external static String renamedMethod(); } -void main() { - eval(''' - globalThis.ExternalStatic = function ExternalStatic(a, b) { - var len = arguments.length; - this.a = len < 1 ? 0 : a; - this.b = len < 2 ? '' : b; - } - globalThis.ExternalStatic.method = function() { - return 'method'; - } - globalThis.ExternalStatic.differentArgsMethod = function(a, b) { - return a + b; - } - globalThis.ExternalStatic.field = 'field'; - globalThis.ExternalStatic.finalField = 'finalField'; - globalThis.ExternalStatic.getSet = 'getSet'; - '''); - +void testStaticMembers() { // Constructors. void testExternalConstructorCall(ExternalStatic externalStatic) { - expect(js_util.getProperty(externalStatic, 'a'), 0); - expect(js_util.getProperty(externalStatic, 'b'), ''); + expect((externalStatic['a'] as JSNumber).toDartInt, 0); + expect((externalStatic['b'] as JSString).toDart, ''); } testExternalConstructorCall(ExternalStatic()); testExternalConstructorCall(ExternalStatic.factory()); testExternalConstructorCall(ExternalStatic.multipleArgs(0, '')); - testExternalConstructorCall(ExternalStatic.differentArgs(0)); testExternalConstructorCall(ExternalStatic.nonExternal()); // Fields. @@ -88,6 +74,69 @@ void main() { // Methods. expect(ExternalStatic.method(), 'method'); - expect(ExternalStatic.differentArgsMethod('method'), 'methodundefined'); expect(ExternalStatic.renamedMethod(), 'method'); } + +void testNamespacedStaticMembers() { + // Constructors. + void testExternalConstructorCall(namespace.ExternalStatic externalStatic) { + expect((externalStatic['a'] as JSNumber).toDartInt, 0); + expect((externalStatic['b'] as JSString).toDart, ''); + } + + testExternalConstructorCall(namespace.ExternalStatic()); + testExternalConstructorCall(namespace.ExternalStatic.factory()); + testExternalConstructorCall(namespace.ExternalStatic.multipleArgs(0, '')); + testExternalConstructorCall(namespace.ExternalStatic.nonExternal()); + + // Fields. + expect(namespace.ExternalStatic.field, 'field'); + namespace.ExternalStatic.field = 'modified'; + expect(namespace.ExternalStatic.field, 'modified'); + expect(namespace.ExternalStatic.renamedField, 'modified'); + namespace.ExternalStatic.renamedField = 'renamedField'; + expect(namespace.ExternalStatic.renamedField, 'renamedField'); + expect(namespace.ExternalStatic.finalField, 'finalField'); + + // Getters and setters. + expect(namespace.ExternalStatic.getSet, 'getSet'); + namespace.ExternalStatic.getSet = 'modified'; + expect(namespace.ExternalStatic.getSet, 'modified'); + expect(namespace.ExternalStatic.renamedGetSet, 'modified'); + namespace.ExternalStatic.renamedGetSet = 'renamedGetSet'; + expect(namespace.ExternalStatic.renamedGetSet, 'renamedGetSet'); + + // Methods. + expect(namespace.ExternalStatic.method(), 'method'); + expect(namespace.ExternalStatic.renamedMethod(), 'method'); +} + +void main() { + eval(''' + globalThis.ExternalStatic = function ExternalStatic(a, b) { + var len = arguments.length; + this.a = len < 1 ? 0 : a; + this.b = len < 2 ? '' : b; + } + globalThis.ExternalStatic.method = function() { + return 'method'; + } + globalThis.ExternalStatic.field = 'field'; + globalThis.ExternalStatic.finalField = 'finalField'; + globalThis.ExternalStatic.getSet = 'getSet'; + '''); + testStaticMembers(); + eval(''' + var library3 = {}; + var library2 = {library3: library3}; + var library1 = {library2: library2}; + globalThis.library1 = library1; + + library3.ExternalStatic = globalThis.ExternalStatic; + library3.ExternalStatic.field = 'field'; + library3.ExternalStatic.finalField = 'finalField'; + library3.ExternalStatic.getSet = 'getSet'; + delete globalThis.ExternalStatic; + '''); + testNamespacedStaticMembers(); +} diff --git a/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces.dart b/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces.dart new file mode 100644 index 000000000000..42ea0518efa2 --- /dev/null +++ b/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2024, 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. + +@JS('library1.library2') +library external_static_member_with_namespaces_test; + +import 'dart:js_interop'; + +@JS() +external void eval(String code); + +@JS('library3.ExternalStatic') +extension type ExternalStatic._(JSObject obj) implements JSObject { + external ExternalStatic(); + external factory ExternalStatic.factory(); + external ExternalStatic.multipleArgs(double a, String b); + ExternalStatic.nonExternal() : this.obj = ExternalStatic() as JSObject; + + external static String field; + @JS('field') + external static String renamedField; + external static final String finalField; + + external static String get getSet; + external static set getSet(String val); + @JS('getSet') + external static String get renamedGetSet; + @JS('getSet') + external static set renamedGetSet(String val); + + external static String method(); + @JS('method') + external static String renamedMethod(); +} diff --git a/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces_test.dart b/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces_test.dart deleted file mode 100644 index d7d02fd51f98..000000000000 --- a/tests/lib/js/static_interop_test/extension_type/external_static_member_with_namespaces_test.dart +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2023, 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. - -@JS('library1.library2') -library external_static_member_with_namespaces_test; - -import 'dart:js_interop'; -import 'dart:js_util' as js_util; - -import 'package:expect/minitest.dart'; - -@JS() -external void eval(String code); - -@JS('library3.ExternalStatic') -extension type ExternalStatic._(JSObject obj) implements Object { - external ExternalStatic(); - external factory ExternalStatic.factory(); - external ExternalStatic.multipleArgs(double a, String b); - external ExternalStatic.differentArgs(double a, [String b = '']); - ExternalStatic.nonExternal() : this.obj = ExternalStatic() as JSObject; - - external static String field; - @JS('field') - external static String renamedField; - external static final String finalField; - - external static String get getSet; - external static set getSet(String val); - @JS('getSet') - external static String get renamedGetSet; - @JS('getSet') - external static set renamedGetSet(String val); - - external static String method(); - external static String differentArgsMethod(String a, [String b = '']); - @JS('method') - external static String renamedMethod(); -} - -void main() { - // Use `callMethod` instead of top-level external to `eval` since the library - // is namespaced. - js_util.callMethod(js_util.globalThis, 'eval', [ - ''' - var library3 = {}; - var library2 = {library3: library3}; - var library1 = {library2: library2}; - globalThis.library1 = library1; - - library3.ExternalStatic = function ExternalStatic(a, b) { - var len = arguments.length; - this.a = len < 1 ? 0 : a; - this.b = len < 2 ? '' : b; - } - library3.ExternalStatic.method = function() { - return 'method'; - } - library3.ExternalStatic.differentArgsMethod = function(a, b) { - return a + b; - } - library3.ExternalStatic.field = 'field'; - library3.ExternalStatic.finalField = 'finalField'; - library3.ExternalStatic.getSet = 'getSet'; - ''' - ]); - - // Constructors. - void testExternalConstructorCall(ExternalStatic externalStatic) { - expect(js_util.getProperty(externalStatic, 'a'), 0); - expect(js_util.getProperty(externalStatic, 'b'), ''); - } - - testExternalConstructorCall(ExternalStatic()); - testExternalConstructorCall(ExternalStatic.factory()); - testExternalConstructorCall(ExternalStatic.multipleArgs(0, '')); - testExternalConstructorCall(ExternalStatic.differentArgs(0)); - testExternalConstructorCall(ExternalStatic.nonExternal()); - - // Fields. - expect(ExternalStatic.field, 'field'); - ExternalStatic.field = 'modified'; - expect(ExternalStatic.field, 'modified'); - expect(ExternalStatic.renamedField, 'modified'); - ExternalStatic.renamedField = 'renamedField'; - expect(ExternalStatic.renamedField, 'renamedField'); - expect(ExternalStatic.finalField, 'finalField'); - - // Getters and setters. - expect(ExternalStatic.getSet, 'getSet'); - ExternalStatic.getSet = 'modified'; - expect(ExternalStatic.getSet, 'modified'); - expect(ExternalStatic.renamedGetSet, 'modified'); - ExternalStatic.renamedGetSet = 'renamedGetSet'; - expect(ExternalStatic.renamedGetSet, 'renamedGetSet'); - - // Methods. - expect(ExternalStatic.method(), 'method'); - expect(ExternalStatic.differentArgsMethod('method'), 'methodundefined'); - expect(ExternalStatic.renamedMethod(), 'method'); -} diff --git a/tests/lib/js/static_interop_test/external_static_member_lowerings_test.dart b/tests/lib/js/static_interop_test/external_static_member_lowerings_test.dart index 730d93ff7147..a13946890cb9 100644 --- a/tests/lib/js/static_interop_test/external_static_member_lowerings_test.dart +++ b/tests/lib/js/static_interop_test/external_static_member_lowerings_test.dart @@ -9,6 +9,8 @@ import 'dart:js_interop'; import 'package:expect/minitest.dart'; +import 'external_static_member_lowerings_with_namespaces.dart' as namespace; + @JS() external void eval(String code); @@ -33,7 +35,6 @@ class ExternalStatic { external static set renamedGetSet(String val); external static String method(); - external static String differentArgsMethod(String a, [String b = '']); @JS('method') external static String renamedMethod(); } @@ -63,41 +64,9 @@ external set renamedGetSet(String val); // Top-level methods. @JS() external String method(); -@JS() -external String differentArgsMethod(String a, [String b = '']); @JS('method') external String renamedMethod(); -void main() { - eval(''' - globalThis.ExternalStatic = function ExternalStatic(initialValue) { - this.initialValue = initialValue; - } - globalThis.ExternalStatic.method = function() { - return 'method'; - } - globalThis.ExternalStatic.differentArgsMethod = function(a, b) { - return a + b; - } - globalThis.ExternalStatic.field = 'field'; - globalThis.ExternalStatic.finalField = 'finalField'; - globalThis.ExternalStatic.getSet = 'getSet'; - - globalThis.field = 'field'; - globalThis.finalField = 'finalField'; - globalThis.getSet = 'getSet'; - globalThis.method = function() { - return 'method'; - } - globalThis.differentArgsMethod = function(a, b) { - return a + b; - } - '''); - testClassStaticMembers(); - testTopLevelMembers(); - testFactories(); -} - void testClassStaticMembers() { // Fields. expect(ExternalStatic.field, 'field'); @@ -118,7 +87,6 @@ void testClassStaticMembers() { // Methods. expect(ExternalStatic.method(), 'method'); - expect(ExternalStatic.differentArgsMethod('method'), 'methodundefined'); expect(ExternalStatic.renamedMethod(), 'method'); } @@ -142,7 +110,6 @@ void testTopLevelMembers() { // Methods. expect(method(), 'method'); - expect(differentArgsMethod('method'), 'methodundefined'); expect(renamedMethod(), 'method'); } @@ -155,3 +122,109 @@ void testFactories() { externalStatic = ExternalStatic.named(); expect(externalStatic.initialValue, null); } + +void testNamespacedClassStaticMembers() { + // Fields. + expect(namespace.ExternalStatic.field, 'field'); + namespace.ExternalStatic.field = 'modified'; + expect(namespace.ExternalStatic.field, 'modified'); + expect(namespace.ExternalStatic.renamedField, 'modified'); + namespace.ExternalStatic.renamedField = 'renamedField'; + expect(namespace.ExternalStatic.renamedField, 'renamedField'); + expect(namespace.ExternalStatic.finalField, 'finalField'); + + // Getters and setters. + expect(namespace.ExternalStatic.getSet, 'getSet'); + namespace.ExternalStatic.getSet = 'modified'; + expect(namespace.ExternalStatic.getSet, 'modified'); + expect(namespace.ExternalStatic.renamedGetSet, 'modified'); + namespace.ExternalStatic.renamedGetSet = 'renamedGetSet'; + expect(namespace.ExternalStatic.renamedGetSet, 'renamedGetSet'); + + // Methods. + expect(namespace.ExternalStatic.method(), 'method'); + expect(namespace.ExternalStatic.renamedMethod(), 'method'); +} + +void testNamespacedTopLevelMembers() { + // Fields. + expect(namespace.field, 'field'); + namespace.field = 'modified'; + expect(namespace.field, 'modified'); + expect(namespace.renamedField, 'modified'); + namespace.renamedField = 'renamedField'; + expect(namespace.renamedField, 'renamedField'); + expect(namespace.finalField, 'finalField'); + + // Getters and setters. + expect(namespace.getSet, 'getSet'); + namespace.getSet = 'modified'; + expect(namespace.getSet, 'modified'); + expect(namespace.renamedGetSet, 'modified'); + namespace.renamedGetSet = 'renamedGetSet'; + expect(namespace.renamedGetSet, 'renamedGetSet'); + + // Methods. + expect(namespace.method(), 'method'); + expect(namespace.renamedMethod(), 'method'); +} + +void testNamespacedFactories() { + // Non-object literal factories. + var initialized = 'initialized'; + + var externalStatic = namespace.ExternalStatic(initialized); + expect(externalStatic.initialValue, initialized); + externalStatic = namespace.ExternalStatic.named(); + expect(externalStatic.initialValue, null); +} + +void main() { + eval(''' + globalThis.ExternalStatic = function ExternalStatic(initialValue) { + this.initialValue = initialValue; + } + globalThis.ExternalStatic.field = 'field'; + globalThis.ExternalStatic.finalField = 'finalField'; + globalThis.ExternalStatic.getSet = 'getSet'; + globalThis.ExternalStatic.method = function() { + return 'method'; + } + + globalThis.field = 'field'; + globalThis.finalField = 'finalField'; + globalThis.getSet = 'getSet'; + globalThis.method = function() { + return 'method'; + } + '''); + testClassStaticMembers(); + testTopLevelMembers(); + testFactories(); + // Move declarations to a namespace and delete the top-level ones to test that + // we use the declaration's enclosing library's annotation and not the current + // library's. + eval(''' + var library3 = {}; + var library2 = {library3: library3}; + var library1 = {library2: library2}; + globalThis.library1 = library1; + + library3.ExternalStatic = globalThis.ExternalStatic; + library3.ExternalStatic.field = 'field'; + library3.ExternalStatic.finalField = 'finalField'; + library3.ExternalStatic.getSet = 'getSet'; + delete globalThis.ExternalStatic; + library3.field = 'field'; + library3.finalField = 'finalField'; + library3.getSet = 'getSet'; + library3.method = globalThis.method; + delete globalThis.field; + delete globalThis.finalField; + delete globalThis.getSet; + delete globalThis.method; + '''); + testNamespacedClassStaticMembers(); + testNamespacedTopLevelMembers(); + testNamespacedFactories(); +} diff --git a/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces.dart b/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces.dart new file mode 100644 index 000000000000..8c515ebde28b --- /dev/null +++ b/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces.dart @@ -0,0 +1,64 @@ +// Copyright (c) 2024, 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. + +// Like `external_static_member_lowerings_test.dart`, but uses the namespaces +// in the `@JS` annotations instead. + +@JS('library1.library2') +library external_static_member_lowerings_with_namespaces_test; + +import 'dart:js_interop'; + +@JS('library3.ExternalStatic') +@staticInterop +class ExternalStatic { + external factory ExternalStatic(String initialValue); + external factory ExternalStatic.named( + [String initialValue = 'uninitialized']); + // External redirecting factories are not allowed. + + external static String field; + @JS('field') + external static String renamedField; + external static final String finalField; + + external static String get getSet; + external static set getSet(String val); + @JS('getSet') + external static String get renamedGetSet; + @JS('getSet') + external static set renamedGetSet(String val); + + external static String method(); + @JS('method') + external static String renamedMethod(); +} + +extension ExternalStaticExtension on ExternalStatic { + external String? get initialValue; +} + +// Top-level fields. +@JS('library3.field') +external String field; +@JS('library3.field') +external String renamedField; +@JS('library3.finalField') +external final String finalField; + +// Top-level getters and setters. +@JS('library3.getSet') +external String get getSet; +@JS('library3.getSet') +external set getSet(String val); +@JS('library3.getSet') +external String get renamedGetSet; +@JS('library3.getSet') +external set renamedGetSet(String val); + +// Top-level methods. +@JS('library3.method') +external String method(); +@JS('library3.method') +external String renamedMethod(); diff --git a/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces_test.dart b/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces_test.dart deleted file mode 100644 index 676220a16fbb..000000000000 --- a/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces_test.dart +++ /dev/null @@ -1,174 +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. - -// Like `external_static_member_lowerings_test.dart`, but uses the namespaces -// in the `@JS` annotations instead. - -@JS('library1.library2') -library external_static_member_lowerings_with_namespaces_test; - -import 'dart:js_interop'; -import 'dart:js_util' as js_util; - -import 'package:expect/minitest.dart'; - -@JS('library3.ExternalStatic') -@staticInterop -class ExternalStatic { - external factory ExternalStatic(String initialValue); - external factory ExternalStatic.named( - [String initialValue = 'uninitialized']); - // External redirecting factories are not allowed. - - external static String field; - @JS('field') - external static String renamedField; - external static final String finalField; - - external static String get getSet; - external static set getSet(String val); - @JS('getSet') - external static String get renamedGetSet; - @JS('getSet') - external static set renamedGetSet(String val); - - external static String method(); - external static String differentArgsMethod(String a, [String b = '']); - @JS('method') - external static String renamedMethod(); -} - -extension on ExternalStatic { - external String? get initialValue; -} - -void main() { - // Use `callMethod` instead of top-level external to `eval` since the library - // is namespaced. - js_util.callMethod(js_util.globalThis, 'eval', [ - ''' - var library3 = {}; - var library2 = {library3: library3}; - var library1 = {library2: library2}; - globalThis.library1 = library1; - - library3.ExternalStatic = function ExternalStatic(initialValue) { - this.initialValue = initialValue; - } - library3.ExternalStatic.method = function() { - return 'method'; - } - library3.ExternalStatic.differentArgsMethod = function(a, b) { - return a + b; - } - library3.ExternalStatic.field = 'field'; - library3.ExternalStatic.finalField = 'finalField'; - library3.ExternalStatic.getSet = 'getSet'; - - library2.field = 'field'; - library2.getSet = 'getSet'; - library2.method = function() { - return 'method'; - } - library2.differentArgsMethod = function(a, b) { - return a + b; - } - library3.namespacedField = 'namespacedField'; - library3.namespacedGetSet = 'namespacedGetSet'; - library3.namespacedMethod = function() { - return 'namespacedMethod'; - } - - ''' - ]); - testClassStaticMembers(); - testTopLevelMembers(); - testFactories(); -} - -// Top-level fields. -@JS() -external String field; -@JS('library3.namespacedField') -external String namespacedField; -@JS('field') -external final String finalField; - -// Top-level getters and setters. -@JS() -external String get getSet; -@JS() -external set getSet(String val); -@JS('library3.namespacedGetSet') -external String get namespacedGetSet; -@JS('library3.namespacedGetSet') -external set namespacedGetSet(String val); - -// Top-level methods. -@JS() -external String method(); -@JS() -external String differentArgsMethod(String a, [String b = '']); -@JS('library3.namespacedMethod') -external String namespacedMethod(); - -void testClassStaticMembers() { - // Fields. - expect(ExternalStatic.field, 'field'); - ExternalStatic.field = 'modified'; - expect(ExternalStatic.field, 'modified'); - expect(ExternalStatic.renamedField, 'modified'); - ExternalStatic.renamedField = 'renamedField'; - expect(ExternalStatic.renamedField, 'renamedField'); - expect(ExternalStatic.finalField, 'finalField'); - - // Getters and setters. - expect(ExternalStatic.getSet, 'getSet'); - ExternalStatic.getSet = 'modified'; - expect(ExternalStatic.getSet, 'modified'); - expect(ExternalStatic.renamedGetSet, 'modified'); - ExternalStatic.renamedGetSet = 'renamedGetSet'; - expect(ExternalStatic.renamedGetSet, 'renamedGetSet'); - - // Methods. - expect(ExternalStatic.method(), 'method'); - expect(ExternalStatic.differentArgsMethod('method'), 'methodundefined'); - expect(ExternalStatic.renamedMethod(), 'method'); -} - -void testTopLevelMembers() { - // Test a variety of renaming and namespacing to make sure we're handling '.' - // correctly. - // Fields. - expect(field, 'field'); - field = 'modified'; - expect(field, 'modified'); - expect(namespacedField, 'namespacedField'); - namespacedField = 'modified'; - expect(namespacedField, 'modified'); - expect(finalField, 'modified'); - - // Getters and setters. - expect(getSet, 'getSet'); - getSet = 'modified'; - expect(getSet, 'modified'); - expect(namespacedGetSet, 'namespacedGetSet'); - namespacedGetSet = 'modified'; - expect(namespacedGetSet, 'modified'); - - // Methods. - expect(method(), 'method'); - expect(differentArgsMethod('method'), 'methodundefined'); - expect(namespacedMethod(), 'namespacedMethod'); -} - -void testFactories() { - // Non-object literal factories. - var initialized = 'initialized'; - - var externalStatic = ExternalStatic(initialized); - expect(externalStatic.initialValue, initialized); - externalStatic = ExternalStatic.named(); - expect(externalStatic.initialValue, null); -} diff --git a/tests/lib/js/static_interop_test/js_default_test.dart b/tests/lib/js/static_interop_test/js_default_test.dart index 90f1c23f248e..c87df208bf60 100644 --- a/tests/lib/js/static_interop_test/js_default_test.dart +++ b/tests/lib/js/static_interop_test/js_default_test.dart @@ -12,7 +12,7 @@ import 'dart:js_interop'; import 'package:expect/minitest.dart'; -import 'js_default_other_library.dart' as other; +import 'js_default_with_namespaces.dart' as namespace; @JS() external void eval(String code); @@ -34,6 +34,20 @@ extension SimpleObjectExtension on SimpleObject { external JSNumber oneOptional(JSNumber n1, [JSNumber n2]); } +@JS('SimpleObject') +extension type SimpleObject2._(JSObject _) implements JSObject { + external factory SimpleObject2(); + external factory SimpleObject2.twoOptional([JSNumber n1, JSNumber n2]); + external factory SimpleObject2.oneOptional(JSNumber n1, [JSNumber n2]); + + external static JSNumber twoOptionalStatic([JSNumber n1, JSNumber n2]); + external static JSNumber oneOptionalStatic(JSNumber n1, [JSNumber n2]); + + external JSNumber get initialArguments; + external JSNumber twoOptional([JSNumber n1, JSNumber n2]); + external JSNumber oneOptional(JSNumber n1, [JSNumber n2]); +} + @JS() external JSNumber twoOptional([JSNumber n1, JSNumber n2]); @@ -75,30 +89,77 @@ void testCurrentLibrary() { expect(1, s.oneOptional(4.0.toJS).toDartInt); expect(2, s.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); + + // Test extension type factories. + expect(0, SimpleObject2.twoOptional().initialArguments.toDartInt); + expect(1, SimpleObject2.twoOptional(4.0.toJS).initialArguments.toDartInt); + expect(2, + SimpleObject2.twoOptional(4.0.toJS, 5.0.toJS).initialArguments.toDartInt); + + expect(1, SimpleObject2.oneOptional(4.0.toJS).initialArguments.toDartInt); + expect(2, + SimpleObject2.oneOptional(4.0.toJS, 5.0.toJS).initialArguments.toDartInt); + + // Test extension type static methods. + expect(0, SimpleObject2.twoOptionalStatic().toDartInt); + expect(1, SimpleObject2.twoOptionalStatic(4.0.toJS).toDartInt); + expect(2, SimpleObject2.twoOptionalStatic(4.0.toJS, 5.0.toJS).toDartInt); + + expect(1, SimpleObject2.oneOptionalStatic(4.0.toJS).toDartInt); + expect(2, SimpleObject2.oneOptionalStatic(4.0.toJS, 5.0.toJS).toDartInt); + + // Test extension type methods. + final s2 = SimpleObject2(); + expect(0, s2.twoOptional().toDartInt); + expect(1, s2.twoOptional(4.0.toJS).toDartInt); + expect(2, s2.twoOptional(4.0.toJS, 5.0.toJS).toDartInt); + + expect(1, s2.oneOptional(4.0.toJS).toDartInt); + expect(2, s2.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); } void testOtherLibrary() { // Test top level methods. - expect(1, other.oneOptional(4.0.toJS).toDartInt); - expect(2, other.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); + expect(1, namespace.oneOptional(4.0.toJS).toDartInt); + expect(2, namespace.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); // Test factories. - expect( - 1, other.SimpleObject.oneOptional(4.0.toJS).initialArguments.toDartInt); + expect(1, + namespace.SimpleObject.oneOptional(4.0.toJS).initialArguments.toDartInt); expect( 2, - other.SimpleObject.oneOptional(4.0.toJS, 5.0.toJS) + namespace.SimpleObject.oneOptional(4.0.toJS, 5.0.toJS) .initialArguments .toDartInt); // Test static methods. - expect(1, other.SimpleObject.oneOptionalStatic(4.0.toJS).toDartInt); - expect(2, other.SimpleObject.oneOptionalStatic(4.0.toJS, 5.0.toJS).toDartInt); + expect(1, namespace.SimpleObject.oneOptionalStatic(4.0.toJS).toDartInt); + expect(2, + namespace.SimpleObject.oneOptionalStatic(4.0.toJS, 5.0.toJS).toDartInt); // Test extension methods. - final s = other.SimpleObject(); + final s = namespace.SimpleObject(); expect(1, s.oneOptional(4.0.toJS).toDartInt); expect(2, s.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); + + // Test extension type factories. + expect(1, + namespace.SimpleObject.oneOptional(4.0.toJS).initialArguments.toDartInt); + expect( + 2, + namespace.SimpleObject.oneOptional(4.0.toJS, 5.0.toJS) + .initialArguments + .toDartInt); + + // Test extension type static methods. + expect(1, namespace.SimpleObject.oneOptionalStatic(4.0.toJS).toDartInt); + expect(2, + namespace.SimpleObject.oneOptionalStatic(4.0.toJS, 5.0.toJS).toDartInt); + + // Test extension type methods. + final s2 = namespace.SimpleObject(); + expect(1, s2.oneOptional(4.0.toJS).toDartInt); + expect(2, s2.oneOptional(4.0.toJS, 5.0.toJS).toDartInt); } void main() { @@ -127,5 +188,19 @@ void main() { } '''); testCurrentLibrary(); + // Move the declarations to a namespace and delete the declarations on + // globalThis to make sure we incorporate the library prefix in + // invocation-level lowering. + eval(''' + var library1 = {}; + globalThis.library1 = library1; + + library1.twoOptional = globalThis.twoOptional; + library1.oneOptional = globalThis.oneOptional; + delete globalThis.twoOptional; + delete globalThis.oneOptional; + library1.SimpleObject = globalThis.SimpleObject; + delete globalThis.SimpleObject; + '''); testOtherLibrary(); } diff --git a/tests/lib/js/static_interop_test/js_default_other_library.dart b/tests/lib/js/static_interop_test/js_default_with_namespaces.dart similarity index 56% rename from tests/lib/js/static_interop_test/js_default_other_library.dart rename to tests/lib/js/static_interop_test/js_default_with_namespaces.dart index fa769a3c39e9..a42b9785cc26 100644 --- a/tests/lib/js/static_interop_test/js_default_other_library.dart +++ b/tests/lib/js/static_interop_test/js_default_with_namespaces.dart @@ -1,8 +1,9 @@ -// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2024, 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 js_default_other_library; +@JS('library1') +library js_default_with_namespaces; import 'dart:js_interop'; @@ -20,5 +21,16 @@ extension SimpleObjectExtension on SimpleObject { external JSNumber oneOptional(JSNumber n1, [JSNumber n2]); } +@JS('SimpleObject') +extension type SimpleObject2._(JSObject _) implements JSObject { + external factory SimpleObject2(); + external factory SimpleObject2.oneOptional(JSNumber n1, [JSNumber n2]); + + external static JSNumber oneOptionalStatic(JSNumber n1, [JSNumber n2]); + + external JSNumber get initialArguments; + external JSNumber oneOptional(JSNumber n1, [JSNumber n2]); +} + @JS() external JSNumber oneOptional(JSNumber n1, [JSNumber n2]);