From 2a669c571f2bf66de2099d3ca2201a7365bf692c Mon Sep 17 00:00:00 2001 From: Srujan Gaddam Date: Tue, 5 Sep 2023 16:05:08 +0000 Subject: [PATCH] [dart:js_interop] Re-enable type checks on external APIs External APIs when using dart:js_interop should only allow primitives, JS types, and other static interop types. This was previously checked in a more restrictive mode called "strict mode" but is not checked everywhere where dart:js_interop APIs exist. Change-Id: Ic82a3ec0bf6062c25d7f8933e503820a21bc191f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316867 Reviewed-by: Sigmund Cherem Commit-Queue: Srujan Gaddam --- CHANGELOG.md | 7 +- .../lib/js_interop_checks.dart | 110 ++++++++++-------- pkg/dart2wasm/lib/target.dart | 2 +- .../src/fasta/fasta_codes_cfe_generated.dart | 63 +++++----- pkg/front_end/messages.status | 4 +- pkg/front_end/messages.yaml | 8 +- .../test/spell_checking_list_messages.txt | 1 + .../external_extension_members_test.dart | 10 +- ...al_member_type_parameters_static_test.dart | 48 ++++---- ...external_static_member_lowerings_test.dart | 2 +- ...atic_member_lowerings_trusttypes_test.dart | 2 +- .../external_extension_member_test.dart | 2 +- .../inline_class/external_member_test.dart | 2 +- .../external_static_member_test.dart | 2 +- ...al_static_member_with_namespaces_test.dart | 2 +- .../js/static_interop_test/js_array_test.dart | 2 +- .../jsobject_type_test.dart | 4 +- .../native_error_test.dart | 2 +- .../operator_static_test.dart | 72 ++++++------ .../static_interop_test/strict_mode_test.dart | 73 +++++------- ...p_level_member_annotation_static_test.dart | 4 +- .../static_interop_test/typed_data_test.dart | 2 +- .../external_extension_members_test.dart | 10 +- ...al_member_type_parameters_static_test.dart | 27 +++-- ...external_static_member_lowerings_test.dart | 2 +- ...atic_member_lowerings_trusttypes_test.dart | 2 +- 26 files changed, 233 insertions(+), 232 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4df70e5b4a..fe393dc9992 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,7 +96,7 @@ constraint][language version] lower bound to 3.2 or greater (`sdk: '^3.2.0'`). #### `dart:js_interop` -- **JSNumber.toDart and Object.toJS**: +- **Breaking Change on JSNumber.toDart and Object.toJS**: `JSNumber.toDart` is removed in favor of `toDartDouble` and `toDartInt` to make the type explicit. `Object.toJS` is also removed in favor of `Object.toJSBox`. Previously, this function would allow Dart objects to flow @@ -128,6 +128,11 @@ constraint][language version] lower bound to 3.2 or greater (`sdk: '^3.2.0'`). number of cases, like when using older browser versions. `dart:js_interop`'s `globalJSObject` is also renamed to `globalContext` and returns the global context used in the lowerings. +- **Breaking Change on Types of `dart:js_interop` External APIs**: + External JS interop APIs when using `dart:js_interop` are restricted to a set + of allowed types. Namely, this include the primitive types like `String`, JS + types from `dart:js_interop`, and other static interop types (either through + `@staticInterop` or extension types). ### Tools diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart index ad2cfb7b214..f28f970de30 100644 --- a/pkg/_js_interop_checks/lib/js_interop_checks.dart +++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart @@ -48,7 +48,7 @@ import 'package:front_end/src/fasta/fasta_codes.dart' show templateJsInteropExtensionTypeNotInterop, templateJsInteropFunctionToJSRequiresStaticType, - templateJsInteropStrictModeViolation; + templateJsInteropStaticInteropExternalTypeViolation; import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/core_types.dart'; @@ -73,18 +73,12 @@ class JsInteropChecks extends RecursiveVisitor { bool _classHasJSAnnotation = false; bool _classHasAnonymousAnnotation = false; bool _classHasStaticInteropAnnotation = false; + final _checkDisallowedInterop = false; bool _inTearoff = false; bool _libraryHasDartJSInteropAnnotation = false; bool _libraryHasJSAnnotation = false; bool _libraryIsGlobalNamespace = false; - // TODO(joshualitt): Today strict mode is just for testing, but we should find - // a way to expose this to users who want strict mode guarantees. - bool _enforceStrictMode = false; - - /// If [enableStrictMode] is true, then static interop methods must use JS - /// types. - final bool enableStrictMode; final ExportChecker exportChecker; final bool isDart2Wasm; @@ -144,7 +138,7 @@ class JsInteropChecks extends RecursiveVisitor { JsInteropChecks(this._coreTypes, ClassHierarchy hierarchy, this._reporter, this._nativeClasses, - {this.isDart2Wasm = false, this.enableStrictMode = false}) + {this.isDart2Wasm = false}) : exportChecker = ExportChecker(_reporter, _coreTypes.objectClass), _functionToJSTarget = _coreTypes.index.getTopLevelProcedure( 'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'), @@ -303,11 +297,11 @@ class JsInteropChecks extends RecursiveVisitor { _libraryHasJSAnnotation = _libraryHasDartJSInteropAnnotation || hasJSInteropAnnotation(node); _libraryIsGlobalNamespace = _isLibraryGlobalNamespace(node); - _enforceStrictMode = _shouldEnforceStrictMode(node); - if (_enforceStrictMode && !node.importUri.isScheme('dart')) { - _checkDisallowedLibrariesInStrictMode(node); - } + // TODO(srujzs): Should we still keep around this check? Currently, it's + // unused since we allow the old interop on dart2wasm, but we should + // disallow them eventually. + if (_checkDisallowedInterop) _checkDisallowedLibrariesForDart2Wasm(node); super.visitLibrary(node); exportChecker.visitLibrary(node); @@ -364,18 +358,6 @@ class JsInteropChecks extends RecursiveVisitor { report(messageJsInteropInvalidStaticClassMemberName); } - // In strict mode, check all types are JS types. - if (enableStrictMode) { - final function = node.function; - _reportProcedureIfNotJSType(function.returnType, node); - for (final parameter in function.positionalParameters) { - _reportProcedureIfNotJSType(parameter.type, node); - } - for (final parameter in function.namedParameters) { - _reportProcedureIfNotJSType(parameter.type, node); - } - } - if (_classHasStaticInteropAnnotation || node.isExtensionTypeMember || node.isExtensionMember || @@ -403,8 +385,16 @@ class JsInteropChecks extends RecursiveVisitor { if (annotatable == null || ((hasDartJSInteropAnnotation(annotatable) || annotatable is ExtensionTypeDeclaration))) { - // Only restrict type parameters for dart:js_interop. + // Checks for dart:js_interop APIs only. _checkStaticInteropMemberUsesValidTypeParameters(node); + final function = node.function; + _reportProcedureIfNotAllowedType(function.returnType, node); + for (final parameter in function.positionalParameters) { + _reportProcedureIfNotAllowedType(parameter.type, node); + } + for (final parameter in function.namedParameters) { + _reportProcedureIfNotAllowedType(parameter.type, node); + } } } } @@ -530,14 +520,7 @@ class JsInteropChecks extends RecursiveVisitor { // JS interop library checks - /// Determine if [node] enforces strict mode checking. This is currently only - /// enabled for testing. - bool _shouldEnforceStrictMode(Library node) { - return node.fileUri.toString().contains(RegExp( - r'(? - _reportIfNotJSType(type, node, node.name, node.fileUri); + void _reportProcedureIfNotAllowedType(DartType type, Procedure node) => + _reportIfNotAllowedExternalType(type, node, node.name, node.fileUri); - void _reportStaticInvocationIfNotJSType( + void _reportStaticInvocationIfNotAllowedType( DartType type, StaticInvocation node) => - _reportIfNotJSType(type, node, node.name, node.location?.file); + _reportIfNotAllowedExternalType( + type, node, node.name, node.location?.file); } /// Visitor used to check that all usages of type parameter types of an external diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart index be7fed74db7..9b2317489c5 100644 --- a/pkg/dart2wasm/lib/target.dart +++ b/pkg/dart2wasm/lib/target.dart @@ -166,7 +166,7 @@ class WasmTarget extends Target { diagnosticReporter as DiagnosticReporter); final jsInteropChecks = JsInteropChecks( coreTypes, hierarchy, jsInteropReporter, _nativeClasses!, - isDart2Wasm: true, enableStrictMode: true); + isDart2Wasm: true); // Process and validate first before doing anything with exports. for (Library library in interopDependentLibraries) { jsInteropChecks.visitLibrary(library); diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart index f60be6c1ddf..2c2e4923f92 100644 --- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart +++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart @@ -4108,6 +4108,38 @@ Message _withArgumentsJsInteropFunctionToJSRequiresStaticType( arguments: {'type': _type}); } +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template + templateJsInteropStaticInteropExternalTypeViolation = const Template< + Message Function(DartType _type, bool isNonNullableByDefault)>( + "JsInteropStaticInteropExternalTypeViolation", + problemMessageTemplate: + r"""Type '#type' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.""", + correctionMessageTemplate: r"""Use one of the valid types instead.""", + withArguments: + _withArgumentsJsInteropStaticInteropExternalTypeViolation); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code + codeJsInteropStaticInteropExternalTypeViolation = + const Code( + "JsInteropStaticInteropExternalTypeViolation", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsJsInteropStaticInteropExternalTypeViolation( + DartType _type, bool isNonNullableByDefault) { + TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault); + List typeParts = labeler.labelType(_type); + String type = typeParts.join(); + return new Message(codeJsInteropStaticInteropExternalTypeViolation, + problemMessage: + """Type '${type}' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.""" + + labeler.originMessages, + correctionMessage: """Use one of the valid types instead.""", + arguments: {'type': _type}); +} + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template templateJsInteropStaticInteropMockNotStaticInteropType = const Template< @@ -4173,37 +4205,6 @@ Message _withArgumentsJsInteropStaticInteropMockTypeParametersNotAllowed( arguments: {'type': _type}); } -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Template - templateJsInteropStrictModeViolation = const Template< - Message Function(DartType _type, bool isNonNullableByDefault)>( - "JsInteropStrictModeViolation", - problemMessageTemplate: - r"""JS interop requires JS types when strict mode is enabled, but Type '#type' is not a type or subtype of a type from `dart:js_interop`.""", - correctionMessageTemplate: r"""Use a JS type instead.""", - withArguments: _withArgumentsJsInteropStrictModeViolation); - -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Code - codeJsInteropStrictModeViolation = - const Code( - "JsInteropStrictModeViolation", -); - -// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -Message _withArgumentsJsInteropStrictModeViolation( - DartType _type, bool isNonNullableByDefault) { - TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault); - List typeParts = labeler.labelType(_type); - String type = typeParts.join(); - return new Message(codeJsInteropStrictModeViolation, - problemMessage: - """JS interop requires JS types when strict mode is enabled, but Type '${type}' is not a type or subtype of a type from `dart:js_interop`.""" + - labeler.originMessages, - correctionMessage: """Use a JS type instead.""", - arguments: {'type': _type}); -} - // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template< Message Function( diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index c84289737a0..9080ed4a3ae 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -624,6 +624,8 @@ JsInteropObjectLiteralConstructorPositionalParameters/analyzerCode: Fail # Web c JsInteropObjectLiteralConstructorPositionalParameters/example: Fail # Web compiler specific JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/analyzerCode: Fail # Web compiler specific JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/example: Fail # Web compiler specific +JsInteropStaticInteropExternalTypeViolation/analyzerCode: Fail # Web compiler specific +JsInteropStaticInteropExternalTypeViolation/example: Fail # Web compiler specific JsInteropStaticInteropGenerativeConstructor/analyzerCode: Fail # Web compiler specific JsInteropStaticInteropGenerativeConstructor/example: Fail # Web compiler specific JsInteropStaticInteropMockMissingGetterOrSetter/analyzerCode: Fail # Web compiler specific @@ -654,8 +656,6 @@ JsInteropStaticInteropWithNonStaticSupertype/analyzerCode: Fail # Web compiler s JsInteropStaticInteropWithNonStaticSupertype/example: Fail # Web compiler specific JsInteropFunctionToJSRequiresStaticType/analyzerCode: Fail # Web compiler specific JsInteropFunctionToJSRequiresStaticType/example: Fail # Web compiler specific -JsInteropStrictModeViolation/analyzerCode: Fail # Web compiler specific -JsInteropStrictModeViolation/example: Fail # Web compiler specific JsInteropStrictModeForbiddenLibrary/analyzerCode: Fail # Web compiler specific JsInteropStrictModeForbiddenLibrary/example: Fail # Web compiler specific LanguageVersionInvalidInDotPackages/analyzerCode: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 461840723ac..a9dba5f751c 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -5669,6 +5669,10 @@ JsInteropStaticInteropExternalMemberWithInvalidTypeParameters: problemMessage: "External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types." correctionMessage: "Try adding a valid bound to the type parameters used in this member." +JsInteropStaticInteropExternalTypeViolation: + problemMessage: "Type '#type' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types." + correctionMessage: "Use one of the valid types instead." + JsInteropStaticInteropGenerativeConstructor: problemMessage: "`@staticInterop` classes should not contain any generative constructors." correctionMessage: "Use factory constructors instead." @@ -5726,10 +5730,6 @@ JsInteropStaticInteropWithNonStaticSupertype: problemMessage: "JS interop class '#name' has an `@staticInterop` annotation, but has supertype '#name2', which does not." correctionMessage: "Try marking the supertype as a static interop class using `@staticInterop`." -JsInteropStrictModeViolation: - problemMessage: "JS interop requires JS types when strict mode is enabled, but Type '#type' is not a type or subtype of a type from `dart:js_interop`." - correctionMessage: "Use a JS type instead." - JsInteropStrictModeForbiddenLibrary: problemMessage: "Library '#name' is forbidden when strict mode is enabled." correctionMessage: "Remove the import of a forbidden library." diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt index e5c2edbce50..72fbec33edf 100644 --- a/pkg/front_end/test/spell_checking_list_messages.txt +++ b/pkg/front_end/test/spell_checking_list_messages.txt @@ -15,6 +15,7 @@ abispecificintegermapping adjusting annotate api +apis argument(s) assigning augment diff --git a/tests/lib/js/static_interop_test/external_extension_members_test.dart b/tests/lib/js/static_interop_test/external_extension_members_test.dart index 1f3b2fae994..fd17276735f 100644 --- a/tests/lib/js/static_interop_test/external_extension_members_test.dart +++ b/tests/lib/js/static_interop_test/external_extension_members_test.dart @@ -12,13 +12,15 @@ import 'dart:js_interop'; import 'package:expect/expect.dart'; import 'package:expect/minitest.dart'; +// To test non-JS types for @staticInterop. +import 'package:js/js.dart' as pkgJs; import 'package:js/js_util.dart' as js_util; @JS() external void eval(String code); -@JS() -@staticInterop +@pkgJs.JS() +@pkgJs.staticInterop class Foo { external factory Foo(int a); } @@ -64,8 +66,8 @@ extension FooExt on Foo { external R combineNestedGeneric(R a, [R b]); } -@JS('module.Bar') -@staticInterop +@pkgJs.JS('module.Bar') +@pkgJs.staticInterop class Bar { external factory Bar(int a); } diff --git a/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart b/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart index b989973100d..3116e417c6a 100644 --- a/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart +++ b/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart @@ -22,6 +22,8 @@ external T invalidTopLevel(T t); typedef Typedef = T Function(); +extension type JSList._(JSAny? _) {} + @JS() @staticInterop class Uninstantiated { @@ -68,12 +70,11 @@ extension UninstantiatedExtension external void consumeV(V v); // Test type parameters in a nested type context. - Set> get getNestedTDart => throw UnimplementedError(); - external Set> get getNestedT; - // ^ - // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types. - external Set> get getNestedU; - external Set> get getNestedV; + JSList> get getNestedTDart => throw UnimplementedError(); + // No error as JSList is an interop extension type. + external JSList> get getNestedT; + external JSList> get getNestedU; + external JSList> get getNestedV; // Test type parameters that are declared by the member. W returnWDart() => throw UnimplementedError(); @@ -126,12 +127,11 @@ extension type UninstantiatedExtensionType> get getNestedTDart => throw UnimplementedError(); - external Set> get getNestedT; - // ^ - // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types. - external Set> get getNestedU; - external Set> get getNestedV; + JSList> get getNestedTDart => throw UnimplementedError(); + // No error as JSList is an interop extension type. + external JSList> get getNestedT; + external JSList> get getNestedU; + external JSList> get getNestedV; // Test type parameters that are declared by the member. W returnWDart() => throw UnimplementedError(); @@ -178,25 +178,25 @@ extension PkgJsExtension on PkgJs { @JS() @staticInterop class Instantiated { - external factory Instantiated(List list); + external factory Instantiated(JSList list); } extension InstantiatedExtension on Instantiated { - external List fieldList; - external List get getList; - external set setList(List list); - external List returnList(); - external void consumeList(List list); + external JSList fieldList; + external JSList get getList; + external set setList(JSList list); + external JSList returnList(); + external void consumeList(JSList list); } extension type InstantiatedExtensionType._(JSObject _) { // Test generic types where all the type parameters are instantiated. - external InstantiatedExtensionType(List list); - external List fieldList; - external List get getList; - external set setList(List list); - external List returnList(); - external void consumeList(List list); + external InstantiatedExtensionType(JSList list); + external JSList fieldList; + external JSList get getList; + external set setList(JSList list); + external JSList returnList(); + external void consumeList(JSList list); } void main() {} 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 0ed69eafbec..730d93ff714 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 @@ -10,7 +10,7 @@ import 'dart:js_interop'; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); @JS() @staticInterop diff --git a/tests/lib/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart b/tests/lib/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart index ec5b1481cb4..aa973cef8a8 100644 --- a/tests/lib/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart +++ b/tests/lib/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart @@ -11,7 +11,7 @@ import 'package:expect/minitest.dart'; import 'package:js/js.dart' show trustTypes; @JS() -external dynamic eval(String code); +external void eval(String code); @JS('ExternalStatic') @staticInterop diff --git a/tests/lib/js/static_interop_test/inline_class/external_extension_member_test.dart b/tests/lib/js/static_interop_test/inline_class/external_extension_member_test.dart index eb0ee58dd9b..25ede604783 100644 --- a/tests/lib/js/static_interop_test/inline_class/external_extension_member_test.dart +++ b/tests/lib/js/static_interop_test/inline_class/external_extension_member_test.dart @@ -12,7 +12,7 @@ import 'dart:js_interop'; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); extension type ExtensionType._(JSObject _) { external ExtensionType(); diff --git a/tests/lib/js/static_interop_test/inline_class/external_member_test.dart b/tests/lib/js/static_interop_test/inline_class/external_member_test.dart index 5280621ab87..18ad3848183 100644 --- a/tests/lib/js/static_interop_test/inline_class/external_member_test.dart +++ b/tests/lib/js/static_interop_test/inline_class/external_member_test.dart @@ -13,7 +13,7 @@ import 'package:expect/expect.dart'; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); extension type External._(JSObject _) { external External(); diff --git a/tests/lib/js/static_interop_test/inline_class/external_static_member_test.dart b/tests/lib/js/static_interop_test/inline_class/external_static_member_test.dart index d598c4f42ff..4c950ea1e8d 100644 --- a/tests/lib/js/static_interop_test/inline_class/external_static_member_test.dart +++ b/tests/lib/js/static_interop_test/inline_class/external_static_member_test.dart @@ -13,7 +13,7 @@ import 'dart:js_util' as js_util; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); @JS() extension type ExternalStatic._(JSObject obj) { diff --git a/tests/lib/js/static_interop_test/inline_class/external_static_member_with_namespaces_test.dart b/tests/lib/js/static_interop_test/inline_class/external_static_member_with_namespaces_test.dart index e37379c707f..59c0d9da24a 100644 --- a/tests/lib/js/static_interop_test/inline_class/external_static_member_with_namespaces_test.dart +++ b/tests/lib/js/static_interop_test/inline_class/external_static_member_with_namespaces_test.dart @@ -13,7 +13,7 @@ import 'dart:js_util' as js_util; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); @JS('library3.ExternalStatic') extension type ExternalStatic._(JSObject obj) { diff --git a/tests/lib/js/static_interop_test/js_array_test.dart b/tests/lib/js/static_interop_test/js_array_test.dart index f32ca42cb0c..f0eb8d9c962 100644 --- a/tests/lib/js/static_interop_test/js_array_test.dart +++ b/tests/lib/js/static_interop_test/js_array_test.dart @@ -10,7 +10,7 @@ library js_array_test; import 'dart:js_interop'; @JS() -external dynamic eval(String code); +external void eval(String code); // dart:js_interop top-levels do return-type checks so if the call to these // getters succeed, it's enough to know they can be interoperable. diff --git a/tests/lib/js/static_interop_test/jsobject_type_test.dart b/tests/lib/js/static_interop_test/jsobject_type_test.dart index 60249eb4e5f..d5e2234e13a 100644 --- a/tests/lib/js/static_interop_test/jsobject_type_test.dart +++ b/tests/lib/js/static_interop_test/jsobject_type_test.dart @@ -12,10 +12,10 @@ import 'dart:js_interop'; import 'package:expect/minitest.dart'; @JS() -external dynamic eval(String code); +external void eval(String code); @JS() -external Object get obj; +external JSAny get obj; void main() { eval(''' diff --git a/tests/lib/js/static_interop_test/native_error_test.dart b/tests/lib/js/static_interop_test/native_error_test.dart index d903cda930b..746424f5214 100644 --- a/tests/lib/js/static_interop_test/native_error_test.dart +++ b/tests/lib/js/static_interop_test/native_error_test.dart @@ -10,7 +10,7 @@ library native_error_test; import 'dart:js_interop'; @JS() -external dynamic eval(String code); +external void eval(String code); // dart:js_interop top-levels do return-type checks so if the call to these // getters succeed, it's enough to know they can be interoperable. diff --git a/tests/lib/js/static_interop_test/operator_static_test.dart b/tests/lib/js/static_interop_test/operator_static_test.dart index 78de569a53d..0c1bb26fe40 100644 --- a/tests/lib/js/static_interop_test/operator_static_test.dart +++ b/tests/lib/js/static_interop_test/operator_static_test.dart @@ -16,60 +16,60 @@ class StaticInterop {} extension _ on StaticInterop { // https://dart.dev/guides/language/language-tour#_operators for the list of // operators allowed by the language. - external void operator <(_); + external void operator <(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >(_); + external void operator >(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator <=(_); + external void operator <=(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >=(_); + external void operator >=(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator -(_); + external void operator -(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator +(_); + external void operator +(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator /(_); + external void operator /(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator ~/(_); + external void operator ~/(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator *(_); + external void operator *(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator %(_); + external void operator %(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator |(_); + external void operator |(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator ^(_); + external void operator ^(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator &(_); + external void operator &(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator <<(_); + external void operator <<(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >>(_); + external void operator >>(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >>>(_); + external void operator >>>(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. @JS('rename') - external void operator [](_); + external void operator [](JSAny _); // ^ // [web] JS interop operator methods cannot be renamed using the '@JS' annotation. @JS('rename') - external void operator []=(_, __); + external void operator []=(JSAny _, JSAny __); // ^ // [web] JS interop operator methods cannot be renamed using the '@JS' annotation. external void operator ~(); @@ -81,60 +81,60 @@ extension _ on StaticInterop { @JS() extension type ExtensionType(JSObject _) { - external void operator <(_); + external void operator <(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >(_); + external void operator >(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator <=(_); + external void operator <=(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >=(_); + external void operator >=(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator -(_); + external void operator -(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator +(_); + external void operator +(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator /(_); + external void operator /(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator ~/(_); + external void operator ~/(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator *(_); + external void operator *(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator %(_); + external void operator %(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator |(_); + external void operator |(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator ^(_); + external void operator ^(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator &(_); + external void operator &(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator <<(_); + external void operator <<(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >>(_); + external void operator >>(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. - external void operator >>>(_); + external void operator >>>(JSAny _); // ^ // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop. @JS('rename') - external void operator [](_); + external void operator [](JSAny _); // ^ // [web] JS interop operator methods cannot be renamed using the '@JS' annotation. @JS('rename') - external void operator []=(_, __); + external void operator []=(JSAny _, JSAny __); // ^ // [web] JS interop operator methods cannot be renamed using the '@JS' annotation. external void operator ~(); diff --git a/tests/lib/js/static_interop_test/strict_mode_test.dart b/tests/lib/js/static_interop_test/strict_mode_test.dart index 868b562415f..e85820dcfcc 100644 --- a/tests/lib/js/static_interop_test/strict_mode_test.dart +++ b/tests/lib/js/static_interop_test/strict_mode_test.dart @@ -8,74 +8,49 @@ library strict_mode_test; import 'dart:js_interop'; -/**/ import 'dart:js'; -// ^ -// [web] Library 'dart:js' is forbidden when strict mode is enabled. - -/**/ import 'dart:js_util'; -// ^ -// [web] Library 'dart:js_util' is forbidden when strict mode is enabled. +import 'dart:js'; +import 'dart:js_util'; @JS() @staticInterop class JSClass { external factory JSClass(List baz); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'List' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'List' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. external factory JSClass.other(Object blu); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'Object' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'Object' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. - external static int foo(); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'int' is not a type or subtype of a type from `dart:js_interop`. - - external static JSClass foo1(String bar); + external static dynamic foo(); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'String' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'dynamic' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. - external static Function foo2(); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'Function' is not a type or subtype of a type from `dart:js_interop`. + external static Function get fooGet; + // ^ + // [web] Type 'Function' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. - external static JSClass foo3(void Function() bar); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'void Function()' is not a type or subtype of a type from `dart:js_interop`. - - external static double get fooGet; - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'double' is not a type or subtype of a type from `dart:js_interop`. - - external static set fooSet(String bar); + external static set fooSet(void Function() bar); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'String' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'void Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. } extension JSClassExtension on JSClass { external dynamic extFoo(); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'dynamic' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'dynamic' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. external JSClass extFoo2(List bar); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'List' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'List' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. - external Function extFoo3(JSClass bar); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'Function' is not a type or subtype of a type from `dart:js_interop`. + external Function get extFooGet; + // ^ + // [web] Type 'Function' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. - external JSClass extFoo4(void Function() bar); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'void Function()' is not a type or subtype of a type from `dart:js_interop`. - - external double get extFooGet; - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'double' is not a type or subtype of a type from `dart:js_interop`. - - external set extFooSet(String bar); + external set extFooSet(void Function() bar); // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'String' is not a type or subtype of a type from `dart:js_interop`. + // [web] Type 'void Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. } @JS() @@ -92,12 +67,16 @@ external void useStaticInteropExtensionType(ExtensionType foo); void main() { jsFunctionTest(((double foo) => 4.0.toJS).toJS); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'double' is not a type or subtype of a type from `dart:js_interop`. jsFunctionTest(((JSNumber foo) => 4.0).toJS); - // ^ - // [web] JS interop requires JS types when strict mode is enabled, but Type 'double' is not a type or subtype of a type from `dart:js_interop`. + + jsFunctionTest(((List foo) => 4.0).toJS); + // ^ + // [web] Type 'List' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. + + jsFunctionTest(((JSNumber foo) => () {}).toJS); + // ^ + // [web] Type 'Null Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types. jsFunctionTest(((((JSNumber foo) => 4.0) as dynamic) as Function).toJS); // ^ diff --git a/tests/lib/js/static_interop_test/top_level_member_annotation_static_test.dart b/tests/lib/js/static_interop_test/top_level_member_annotation_static_test.dart index 198947143e1..f6a8e2037dc 100644 --- a/tests/lib/js/static_interop_test/top_level_member_annotation_static_test.dart +++ b/tests/lib/js/static_interop_test/top_level_member_annotation_static_test.dart @@ -22,7 +22,7 @@ external int get getter; // ^ // [web] Only JS interop members may be 'external'. -external set setter(_); +external set setter(int _); // ^ // [web] Only JS interop members may be 'external'. @@ -40,7 +40,7 @@ external int annotatedFinalField; external int get annotatedGetter; @JS() -external set annotatedSetter(_); +external set annotatedSetter(int _); @JS() external int annotatedMethod(); diff --git a/tests/lib/js/static_interop_test/typed_data_test.dart b/tests/lib/js/static_interop_test/typed_data_test.dart index 3aca176d420..4d66062dfc1 100644 --- a/tests/lib/js/static_interop_test/typed_data_test.dart +++ b/tests/lib/js/static_interop_test/typed_data_test.dart @@ -11,7 +11,7 @@ library typed_data_test; import 'dart:js_interop'; @JS() -external dynamic eval(String code); +external void eval(String code); // dart:js_interop top-levels do return-type checks so if the call to these // getters succeed, it's enough to know they can be interoperable. diff --git a/tests/lib_2/js/static_interop_test/external_extension_members_test.dart b/tests/lib_2/js/static_interop_test/external_extension_members_test.dart index dd5ad9d1863..db80c3fb8bf 100644 --- a/tests/lib_2/js/static_interop_test/external_extension_members_test.dart +++ b/tests/lib_2/js/static_interop_test/external_extension_members_test.dart @@ -14,13 +14,15 @@ import 'dart:js_interop'; import 'package:expect/expect.dart'; import 'package:expect/minitest.dart'; +// To test non-JS types for @staticInterop. +import 'package:js/js.dart' as pkgJs; import 'package:js/js_util.dart' as js_util; @JS() external void eval(String code); -@JS() -@staticInterop +@pkgJs.JS() +@pkgJs.staticInterop class Foo { external factory Foo(int a); } @@ -66,8 +68,8 @@ extension FooExt on Foo { external R combineNestedGeneric(R a, [R b]); } -@JS('module.Bar') -@staticInterop +@pkgJs.JS('module.Bar') +@pkgJs.staticInterop class Bar { external factory Bar(int a); } diff --git a/tests/lib_2/js/static_interop_test/external_member_type_parameters_static_test.dart b/tests/lib_2/js/static_interop_test/external_member_type_parameters_static_test.dart index 1cbac170dc6..ec682eeb223 100644 --- a/tests/lib_2/js/static_interop_test/external_member_type_parameters_static_test.dart +++ b/tests/lib_2/js/static_interop_test/external_member_type_parameters_static_test.dart @@ -20,6 +20,10 @@ external T invalidTopLevel(T t); typedef Typedef = T Function(); +@JS() +@staticInterop +class JSList {} + @JS() @staticInterop class Uninstantiated { @@ -66,12 +70,11 @@ extension UninstantiatedExtension external void consumeV(V v); // Test type parameters in a nested type context. - Set> get getNestedTDart => throw UnimplementedError(); - external Set> get getNestedT; - // ^ - // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types. - external Set> get getNestedU; - external Set> get getNestedV; + JSList> get getNestedTDart => throw UnimplementedError(); + // No error as JSList is an interop type. + external JSList> get getNestedT; + external JSList> get getNestedU; + external JSList> get getNestedV; // Test type parameters that are declared by the member. W returnWDart() => throw UnimplementedError(); @@ -109,15 +112,15 @@ extension PkgJsExtension on PkgJs { @JS() @staticInterop class Instantiated { - external factory Instantiated(List list); + external factory Instantiated(JSList list); } extension InstantiatedExtension on Instantiated { - external List fieldList; - external List get getList; - external set setList(List list); - external List returnList(); - external void consumeList(List list); + external JSList fieldList; + external JSList get getList; + external set setList(JSList list); + external JSList returnList(); + external void consumeList(JSList list); } void main() {} diff --git a/tests/lib_2/js/static_interop_test/external_static_member_lowerings_test.dart b/tests/lib_2/js/static_interop_test/external_static_member_lowerings_test.dart index 837425f7bac..0773976ee46 100644 --- a/tests/lib_2/js/static_interop_test/external_static_member_lowerings_test.dart +++ b/tests/lib_2/js/static_interop_test/external_static_member_lowerings_test.dart @@ -11,7 +11,7 @@ import 'package:expect/minitest.dart'; import 'package:js/js.dart' show staticInterop; @JS() -external dynamic eval(String code); +external void eval(String code); @JS() @staticInterop diff --git a/tests/lib_2/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart b/tests/lib_2/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart index 4bac12bc4f8..b81efb0f43b 100644 --- a/tests/lib_2/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart +++ b/tests/lib_2/js/static_interop_test/external_static_member_lowerings_trusttypes_test.dart @@ -11,7 +11,7 @@ import 'package:expect/minitest.dart'; import 'package:js/js.dart' show trustTypes, staticInterop; @JS() -external dynamic eval(String code); +external void eval(String code); @JS('ExternalStatic') @staticInterop