[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 <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
Srujan Gaddam 2023-09-05 16:05:08 +00:00 committed by Commit Queue
parent cf5336c244
commit 2a669c571f
26 changed files with 233 additions and 232 deletions

View file

@ -96,7 +96,7 @@ constraint][language version] lower bound to 3.2 or greater (`sdk: '^3.2.0'`).
#### `dart:js_interop` #### `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 `JSNumber.toDart` is removed in favor of `toDartDouble` and `toDartInt` to
make the type explicit. `Object.toJS` is also removed in favor of make the type explicit. `Object.toJS` is also removed in favor of
`Object.toJSBox`. Previously, this function would allow Dart objects to flow `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 number of cases, like when using older browser versions. `dart:js_interop`'s
`globalJSObject` is also renamed to `globalContext` and returns the global `globalJSObject` is also renamed to `globalContext` and returns the global
context used in the lowerings. 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 ### Tools

View file

@ -48,7 +48,7 @@ import 'package:front_end/src/fasta/fasta_codes.dart'
show show
templateJsInteropExtensionTypeNotInterop, templateJsInteropExtensionTypeNotInterop,
templateJsInteropFunctionToJSRequiresStaticType, templateJsInteropFunctionToJSRequiresStaticType,
templateJsInteropStrictModeViolation; templateJsInteropStaticInteropExternalTypeViolation;
import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart'; import 'package:kernel/core_types.dart';
@ -73,18 +73,12 @@ class JsInteropChecks extends RecursiveVisitor {
bool _classHasJSAnnotation = false; bool _classHasJSAnnotation = false;
bool _classHasAnonymousAnnotation = false; bool _classHasAnonymousAnnotation = false;
bool _classHasStaticInteropAnnotation = false; bool _classHasStaticInteropAnnotation = false;
final _checkDisallowedInterop = false;
bool _inTearoff = false; bool _inTearoff = false;
bool _libraryHasDartJSInteropAnnotation = false; bool _libraryHasDartJSInteropAnnotation = false;
bool _libraryHasJSAnnotation = false; bool _libraryHasJSAnnotation = false;
bool _libraryIsGlobalNamespace = 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 ExportChecker exportChecker;
final bool isDart2Wasm; final bool isDart2Wasm;
@ -144,7 +138,7 @@ class JsInteropChecks extends RecursiveVisitor {
JsInteropChecks(this._coreTypes, ClassHierarchy hierarchy, this._reporter, JsInteropChecks(this._coreTypes, ClassHierarchy hierarchy, this._reporter,
this._nativeClasses, this._nativeClasses,
{this.isDart2Wasm = false, this.enableStrictMode = false}) {this.isDart2Wasm = false})
: exportChecker = ExportChecker(_reporter, _coreTypes.objectClass), : exportChecker = ExportChecker(_reporter, _coreTypes.objectClass),
_functionToJSTarget = _coreTypes.index.getTopLevelProcedure( _functionToJSTarget = _coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'), 'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
@ -303,11 +297,11 @@ class JsInteropChecks extends RecursiveVisitor {
_libraryHasJSAnnotation = _libraryHasJSAnnotation =
_libraryHasDartJSInteropAnnotation || hasJSInteropAnnotation(node); _libraryHasDartJSInteropAnnotation || hasJSInteropAnnotation(node);
_libraryIsGlobalNamespace = _isLibraryGlobalNamespace(node); _libraryIsGlobalNamespace = _isLibraryGlobalNamespace(node);
_enforceStrictMode = _shouldEnforceStrictMode(node);
if (_enforceStrictMode && !node.importUri.isScheme('dart')) { // TODO(srujzs): Should we still keep around this check? Currently, it's
_checkDisallowedLibrariesInStrictMode(node); // unused since we allow the old interop on dart2wasm, but we should
} // disallow them eventually.
if (_checkDisallowedInterop) _checkDisallowedLibrariesForDart2Wasm(node);
super.visitLibrary(node); super.visitLibrary(node);
exportChecker.visitLibrary(node); exportChecker.visitLibrary(node);
@ -364,18 +358,6 @@ class JsInteropChecks extends RecursiveVisitor {
report(messageJsInteropInvalidStaticClassMemberName); 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 || if (_classHasStaticInteropAnnotation ||
node.isExtensionTypeMember || node.isExtensionTypeMember ||
node.isExtensionMember || node.isExtensionMember ||
@ -403,8 +385,16 @@ class JsInteropChecks extends RecursiveVisitor {
if (annotatable == null || if (annotatable == null ||
((hasDartJSInteropAnnotation(annotatable) || ((hasDartJSInteropAnnotation(annotatable) ||
annotatable is ExtensionTypeDeclaration))) { annotatable is ExtensionTypeDeclaration))) {
// Only restrict type parameters for dart:js_interop. // Checks for dart:js_interop APIs only.
_checkStaticInteropMemberUsesValidTypeParameters(node); _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 // JS interop library checks
/// Determine if [node] enforces strict mode checking. This is currently only void _checkDisallowedLibrariesForDart2Wasm(Library node) {
/// enabled for testing.
bool _shouldEnforceStrictMode(Library node) {
return node.fileUri.toString().contains(RegExp(
r'(?<!generated_)tests/lib/js/static_interop_test/strict_mode_test.dart'));
}
void _checkDisallowedLibrariesInStrictMode(Library node) {
for (final dependency in node.dependencies) { for (final dependency in node.dependencies) {
final dependencyUriString = dependency.targetLibrary.importUri.toString(); final dependencyUriString = dependency.targetLibrary.importUri.toString();
if (_disallowedLibrariesInStrictMode.contains(dependencyUriString)) { if (_disallowedLibrariesInStrictMode.contains(dependencyUriString)) {
@ -735,9 +718,9 @@ class JsInteropChecks extends RecursiveVisitor {
node.name.text.length, node.name.text.length,
node.location?.file); node.location?.file);
} else { } else {
_reportStaticInvocationIfNotJSType(functionType.returnType, node); _reportStaticInvocationIfNotAllowedType(functionType.returnType, node);
for (final parameter in functionType.positionalParameters) { for (final parameter in functionType.positionalParameters) {
_reportStaticInvocationIfNotJSType(parameter, node); _reportStaticInvocationIfNotAllowedType(parameter, node);
} }
} }
} }
@ -946,8 +929,7 @@ class JsInteropChecks extends RecursiveVisitor {
return false; return false;
} }
void _reportIfNotJSType( bool _isAllowedExternalType(DartType type) {
DartType type, TreeNode node, Name name, Uri? fileUri) {
// TODO(joshualitt): We allow only JS types on external JS interop APIs with // TODO(joshualitt): We allow only JS types on external JS interop APIs with
// two exceptions: `void` and `Null`. Both of these exceptions exist largely // two exceptions: `void` and `Null`. Both of these exceptions exist largely
// to support passing Dart functions to JS as callbacks. Furthermore, both // to support passing Dart functions to JS as callbacks. Furthermore, both
@ -955,28 +937,54 @@ class JsInteropChecks extends RecursiveVisitor {
// said, for completeness, we may restrict these two types someday, and // said, for completeness, we may restrict these two types someday, and
// provide JS types equivalents, but likely only if we have implicit // provide JS types equivalents, but likely only if we have implicit
// conversions between Dart types and JS types. // conversions between Dart types and JS types.
if (_enforceStrictMode &&
!(type is VoidType || // Type parameter types are checked elsewhere.
type is NullType || if (type is VoidType || type is NullType || type is TypeParameterType) {
(type is InterfaceType && return true;
hasStaticInteropAnnotation(type.classNode)) || }
(type is ExtensionType && if (type is InterfaceType) {
_extensionIndex final cls = type.classNode;
.isInteropExtensionType(type.extensionTypeDeclaration)))) { if (cls == _coreTypes.boolClass ||
cls == _coreTypes.numClass ||
cls == _coreTypes.doubleClass ||
cls == _coreTypes.intClass ||
cls == _coreTypes.stringClass) {
return true;
}
if (hasStaticInteropAnnotation(cls)) return true;
}
if (type is ExtensionType) {
if (_extensionIndex
.isInteropExtensionType(type.extensionTypeDeclaration)) {
return true;
}
// Extension types where the representation type is allowed are okay.
// TODO(srujzs): Once the CFE pre-computes the concrete type, don't
// recurse.
return _isAllowedExternalType(type.typeErasure);
}
return false;
}
void _reportIfNotAllowedExternalType(
DartType type, TreeNode node, Name name, Uri? fileUri) {
if (!_isAllowedExternalType(type)) {
_reporter.report( _reporter.report(
templateJsInteropStrictModeViolation.withArguments(type, true), templateJsInteropStaticInteropExternalTypeViolation.withArguments(
type, true),
node.fileOffset, node.fileOffset,
name.text.length, name.text.length,
fileUri); fileUri);
} }
} }
void _reportProcedureIfNotJSType(DartType type, Procedure node) => void _reportProcedureIfNotAllowedType(DartType type, Procedure node) =>
_reportIfNotJSType(type, node, node.name, node.fileUri); _reportIfNotAllowedExternalType(type, node, node.name, node.fileUri);
void _reportStaticInvocationIfNotJSType( void _reportStaticInvocationIfNotAllowedType(
DartType type, StaticInvocation node) => 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 /// Visitor used to check that all usages of type parameter types of an external

View file

@ -166,7 +166,7 @@ class WasmTarget extends Target {
diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>); diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>);
final jsInteropChecks = JsInteropChecks( final jsInteropChecks = JsInteropChecks(
coreTypes, hierarchy, jsInteropReporter, _nativeClasses!, coreTypes, hierarchy, jsInteropReporter, _nativeClasses!,
isDart2Wasm: true, enableStrictMode: true); isDart2Wasm: true);
// Process and validate first before doing anything with exports. // Process and validate first before doing anything with exports.
for (Library library in interopDependentLibraries) { for (Library library in interopDependentLibraries) {
jsInteropChecks.visitLibrary(library); jsInteropChecks.visitLibrary(library);

View file

@ -4108,6 +4108,38 @@ Message _withArgumentsJsInteropFunctionToJSRequiresStaticType(
arguments: {'type': _type}); arguments: {'type': _type});
} }
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
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<Message Function(DartType _type, bool isNonNullableByDefault)>
codeJsInteropStaticInteropExternalTypeViolation =
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropStaticInteropExternalTypeViolation",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropStaticInteropExternalTypeViolation(
DartType _type, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> 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. // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)> const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateJsInteropStaticInteropMockNotStaticInteropType = const Template< templateJsInteropStaticInteropMockNotStaticInteropType = const Template<
@ -4173,37 +4205,6 @@ Message _withArgumentsJsInteropStaticInteropMockTypeParametersNotAllowed(
arguments: {'type': _type}); arguments: {'type': _type});
} }
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
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<Message Function(DartType _type, bool isNonNullableByDefault)>
codeJsInteropStrictModeViolation =
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropStrictModeViolation",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropStrictModeViolation(
DartType _type, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> 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. // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template< const Template<
Message Function( Message Function(

View file

@ -624,6 +624,8 @@ JsInteropObjectLiteralConstructorPositionalParameters/analyzerCode: Fail # Web c
JsInteropObjectLiteralConstructorPositionalParameters/example: Fail # Web compiler specific JsInteropObjectLiteralConstructorPositionalParameters/example: Fail # Web compiler specific
JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/analyzerCode: Fail # Web compiler specific JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/example: 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/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropGenerativeConstructor/example: Fail # Web compiler specific JsInteropStaticInteropGenerativeConstructor/example: Fail # Web compiler specific
JsInteropStaticInteropMockMissingGetterOrSetter/analyzerCode: 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 JsInteropStaticInteropWithNonStaticSupertype/example: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/analyzerCode: Fail # Web compiler specific JsInteropFunctionToJSRequiresStaticType/analyzerCode: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/example: 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/analyzerCode: Fail # Web compiler specific
JsInteropStrictModeForbiddenLibrary/example: Fail # Web compiler specific JsInteropStrictModeForbiddenLibrary/example: Fail # Web compiler specific
LanguageVersionInvalidInDotPackages/analyzerCode: Fail LanguageVersionInvalidInDotPackages/analyzerCode: Fail

View file

@ -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." 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." 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: JsInteropStaticInteropGenerativeConstructor:
problemMessage: "`@staticInterop` classes should not contain any generative constructors." problemMessage: "`@staticInterop` classes should not contain any generative constructors."
correctionMessage: "Use factory constructors instead." 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." 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`." 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: JsInteropStrictModeForbiddenLibrary:
problemMessage: "Library '#name' is forbidden when strict mode is enabled." problemMessage: "Library '#name' is forbidden when strict mode is enabled."
correctionMessage: "Remove the import of a forbidden library." correctionMessage: "Remove the import of a forbidden library."

View file

@ -15,6 +15,7 @@ abispecificintegermapping
adjusting adjusting
annotate annotate
api api
apis
argument(s) argument(s)
assigning assigning
augment augment

View file

@ -12,13 +12,15 @@ import 'dart:js_interop';
import 'package:expect/expect.dart'; import 'package:expect/expect.dart';
import 'package:expect/minitest.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; import 'package:js/js_util.dart' as js_util;
@JS() @JS()
external void eval(String code); external void eval(String code);
@JS() @pkgJs.JS()
@staticInterop @pkgJs.staticInterop
class Foo<T extends JSAny?, U extends Nested> { class Foo<T extends JSAny?, U extends Nested> {
external factory Foo(int a); external factory Foo(int a);
} }
@ -64,8 +66,8 @@ extension FooExt<T extends JSAny?, U extends Nested> on Foo<T, U> {
external R combineNestedGeneric<R extends Nested>(R a, [R b]); external R combineNestedGeneric<R extends Nested>(R a, [R b]);
} }
@JS('module.Bar') @pkgJs.JS('module.Bar')
@staticInterop @pkgJs.staticInterop
class Bar { class Bar {
external factory Bar(int a); external factory Bar(int a);
} }

View file

@ -22,6 +22,8 @@ external T invalidTopLevel<T>(T t);
typedef Typedef<T> = T Function(); typedef Typedef<T> = T Function();
extension type JSList<T>._(JSAny? _) {}
@JS() @JS()
@staticInterop @staticInterop
class Uninstantiated<W, X extends Instantiated?> { class Uninstantiated<W, X extends Instantiated?> {
@ -68,12 +70,11 @@ extension UninstantiatedExtension<T, U extends JSAny?, V extends Instantiated>
external void consumeV(V v); external void consumeV(V v);
// Test type parameters in a nested type context. // Test type parameters in a nested type context.
Set<Typedef<T>> get getNestedTDart => throw UnimplementedError(); JSList<Typedef<T>> get getNestedTDart => throw UnimplementedError();
external Set<Typedef<T>> get getNestedT; // No error as JSList is an interop extension type.
// ^ external JSList<Typedef<T>> 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 JSList<Typedef<U>> get getNestedU;
external Set<Typedef<U>> get getNestedU; external JSList<Typedef<V>> get getNestedV;
external Set<Typedef<V>> get getNestedV;
// Test type parameters that are declared by the member. // Test type parameters that are declared by the member.
W returnWDart<W>() => throw UnimplementedError(); W returnWDart<W>() => throw UnimplementedError();
@ -126,12 +127,11 @@ extension type UninstantiatedExtensionType<T, U extends JSAny?,
external void consumeV(V v); external void consumeV(V v);
// Test type parameters in a nested type context. // Test type parameters in a nested type context.
Set<Typedef<T>> get getNestedTDart => throw UnimplementedError(); JSList<Typedef<T>> get getNestedTDart => throw UnimplementedError();
external Set<Typedef<T>> get getNestedT; // No error as JSList is an interop extension type.
// ^ external JSList<Typedef<T>> 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 JSList<Typedef<U>> get getNestedU;
external Set<Typedef<U>> get getNestedU; external JSList<Typedef<V>> get getNestedV;
external Set<Typedef<V>> get getNestedV;
// Test type parameters that are declared by the member. // Test type parameters that are declared by the member.
W returnWDart<W>() => throw UnimplementedError(); W returnWDart<W>() => throw UnimplementedError();
@ -178,25 +178,25 @@ extension PkgJsExtension<T> on PkgJs<T> {
@JS() @JS()
@staticInterop @staticInterop
class Instantiated { class Instantiated {
external factory Instantiated(List<JSNumber> list); external factory Instantiated(JSList<JSNumber> list);
} }
extension InstantiatedExtension on Instantiated { extension InstantiatedExtension on Instantiated {
external List<Instantiated> fieldList; external JSList<int> fieldList;
external List<Instantiated> get getList; external JSList<int> get getList;
external set setList(List<Instantiated> list); external set setList(JSList<int> list);
external List<Instantiated> returnList(); external JSList<int> returnList();
external void consumeList(List<Instantiated> list); external void consumeList(JSList<int> list);
} }
extension type InstantiatedExtensionType._(JSObject _) { extension type InstantiatedExtensionType._(JSObject _) {
// Test generic types where all the type parameters are instantiated. // Test generic types where all the type parameters are instantiated.
external InstantiatedExtensionType(List<JSNumber> list); external InstantiatedExtensionType(JSList<int> list);
external List<InstantiatedExtensionType> fieldList; external JSList<int> fieldList;
external List<InstantiatedExtensionType> get getList; external JSList<int> get getList;
external set setList(List<InstantiatedExtensionType> list); external set setList(JSList<int> list);
external List<InstantiatedExtensionType> returnList(); external JSList<int> returnList();
external void consumeList(List<InstantiatedExtensionType> list); external void consumeList(JSList<int> list);
} }
void main() {} void main() {}

View file

@ -10,7 +10,7 @@ import 'dart:js_interop';
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS() @JS()
@staticInterop @staticInterop

View file

@ -11,7 +11,7 @@ import 'package:expect/minitest.dart';
import 'package:js/js.dart' show trustTypes; import 'package:js/js.dart' show trustTypes;
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS('ExternalStatic') @JS('ExternalStatic')
@staticInterop @staticInterop

View file

@ -12,7 +12,7 @@ import 'dart:js_interop';
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
extension type ExtensionType._(JSObject _) { extension type ExtensionType._(JSObject _) {
external ExtensionType(); external ExtensionType();

View file

@ -13,7 +13,7 @@ import 'package:expect/expect.dart';
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
extension type External<T extends JSAny?, U extends Nested>._(JSObject _) { extension type External<T extends JSAny?, U extends Nested>._(JSObject _) {
external External(); external External();

View file

@ -13,7 +13,7 @@ import 'dart:js_util' as js_util;
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS() @JS()
extension type ExternalStatic._(JSObject obj) { extension type ExternalStatic._(JSObject obj) {

View file

@ -13,7 +13,7 @@ import 'dart:js_util' as js_util;
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS('library3.ExternalStatic') @JS('library3.ExternalStatic')
extension type ExternalStatic._(JSObject obj) { extension type ExternalStatic._(JSObject obj) {

View file

@ -10,7 +10,7 @@ library js_array_test;
import 'dart:js_interop'; import 'dart:js_interop';
@JS() @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 // 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. // getters succeed, it's enough to know they can be interoperable.

View file

@ -12,10 +12,10 @@ import 'dart:js_interop';
import 'package:expect/minitest.dart'; import 'package:expect/minitest.dart';
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS() @JS()
external Object get obj; external JSAny get obj;
void main() { void main() {
eval(''' eval('''

View file

@ -10,7 +10,7 @@ library native_error_test;
import 'dart:js_interop'; import 'dart:js_interop';
@JS() @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 // 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. // getters succeed, it's enough to know they can be interoperable.

View file

@ -16,60 +16,60 @@ class StaticInterop {}
extension _ on StaticInterop { extension _ on StaticInterop {
// https://dart.dev/guides/language/language-tour#_operators for the list of // https://dart.dev/guides/language/language-tour#_operators for the list of
// operators allowed by the language. // 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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop.
@JS('rename') @JS('rename')
external void operator [](_); external void operator [](JSAny _);
// ^ // ^
// [web] JS interop operator methods cannot be renamed using the '@JS' annotation. // [web] JS interop operator methods cannot be renamed using the '@JS' annotation.
@JS('rename') @JS('rename')
external void operator []=(_, __); external void operator []=(JSAny _, JSAny __);
// ^ // ^
// [web] JS interop operator methods cannot be renamed using the '@JS' annotation. // [web] JS interop operator methods cannot be renamed using the '@JS' annotation.
external void operator ~(); external void operator ~();
@ -81,60 +81,60 @@ extension _ on StaticInterop {
@JS() @JS()
extension type ExtensionType(JSObject _) { 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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [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. // [web] JS interop classes do not support operator methods, with the exception of '[]' and '[]=' using static interop.
@JS('rename') @JS('rename')
external void operator [](_); external void operator [](JSAny _);
// ^ // ^
// [web] JS interop operator methods cannot be renamed using the '@JS' annotation. // [web] JS interop operator methods cannot be renamed using the '@JS' annotation.
@JS('rename') @JS('rename')
external void operator []=(_, __); external void operator []=(JSAny _, JSAny __);
// ^ // ^
// [web] JS interop operator methods cannot be renamed using the '@JS' annotation. // [web] JS interop operator methods cannot be renamed using the '@JS' annotation.
external void operator ~(); external void operator ~();

View file

@ -8,74 +8,49 @@
library strict_mode_test; library strict_mode_test;
import 'dart:js_interop'; import 'dart:js_interop';
/**/ import 'dart:js'; import 'dart:js';
// ^ import 'dart:js_util';
// [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.
@JS() @JS()
@staticInterop @staticInterop
class JSClass { class JSClass {
external factory JSClass(List<int> baz); external factory JSClass(List<int> baz);
// ^ // ^
// [web] JS interop requires JS types when strict mode is enabled, but Type 'List<int>' is not a type or subtype of a type from `dart:js_interop`. // [web] Type 'List<int>' 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); 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(); external static dynamic 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`. // [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 JSClass foo1(String bar); external static Function get fooGet;
// ^ // ^
// [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 '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 Function foo2(); external static set fooSet(void Function() 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`. // [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.
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);
// ^
// [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`.
} }
extension JSClassExtension on JSClass { extension JSClassExtension on JSClass {
external dynamic extFoo(); 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<Object?> bar); external JSClass extFoo2(List<Object?> bar);
// ^ // ^
// [web] JS interop requires JS types when strict mode is enabled, but Type 'List<Object?>' is not a type or subtype of a type from `dart:js_interop`. // [web] Type 'List<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 Function extFoo3(JSClass bar); external Function get extFooGet;
// ^ // ^
// [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`. // [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); external set extFooSet(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`. // [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.
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);
// ^
// [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`.
} }
@JS() @JS()
@ -92,12 +67,16 @@ external void useStaticInteropExtensionType(ExtensionType foo);
void main() { void main() {
jsFunctionTest(((double foo) => 4.0.toJS).toJS); 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); jsFunctionTest(((JSNumber foo) => 4.0).toJS);
jsFunctionTest(((List 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`. // [web] Type 'List<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.
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); jsFunctionTest(((((JSNumber foo) => 4.0) as dynamic) as Function).toJS);
// ^ // ^

View file

@ -22,7 +22,7 @@ external int get getter;
// ^ // ^
// [web] Only JS interop members may be 'external'. // [web] Only JS interop members may be 'external'.
external set setter(_); external set setter(int _);
// ^ // ^
// [web] Only JS interop members may be 'external'. // [web] Only JS interop members may be 'external'.
@ -40,7 +40,7 @@ external int annotatedFinalField;
external int get annotatedGetter; external int get annotatedGetter;
@JS() @JS()
external set annotatedSetter(_); external set annotatedSetter(int _);
@JS() @JS()
external int annotatedMethod(); external int annotatedMethod();

View file

@ -11,7 +11,7 @@ library typed_data_test;
import 'dart:js_interop'; import 'dart:js_interop';
@JS() @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 // 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. // getters succeed, it's enough to know they can be interoperable.

View file

@ -14,13 +14,15 @@ import 'dart:js_interop';
import 'package:expect/expect.dart'; import 'package:expect/expect.dart';
import 'package:expect/minitest.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; import 'package:js/js_util.dart' as js_util;
@JS() @JS()
external void eval(String code); external void eval(String code);
@JS() @pkgJs.JS()
@staticInterop @pkgJs.staticInterop
class Foo<T extends JSAny, U extends Nested> { class Foo<T extends JSAny, U extends Nested> {
external factory Foo(int a); external factory Foo(int a);
} }
@ -66,8 +68,8 @@ extension FooExt<T extends JSAny, U extends Nested> on Foo<T, U> {
external R combineNestedGeneric<R extends Nested>(R a, [R b]); external R combineNestedGeneric<R extends Nested>(R a, [R b]);
} }
@JS('module.Bar') @pkgJs.JS('module.Bar')
@staticInterop @pkgJs.staticInterop
class Bar { class Bar {
external factory Bar(int a); external factory Bar(int a);
} }

View file

@ -20,6 +20,10 @@ external T invalidTopLevel<T>(T t);
typedef Typedef<T> = T Function(); typedef Typedef<T> = T Function();
@JS()
@staticInterop
class JSList<T> {}
@JS() @JS()
@staticInterop @staticInterop
class Uninstantiated<W, X extends Instantiated> { class Uninstantiated<W, X extends Instantiated> {
@ -66,12 +70,11 @@ extension UninstantiatedExtension<T, U extends JSAny, V extends Instantiated>
external void consumeV(V v); external void consumeV(V v);
// Test type parameters in a nested type context. // Test type parameters in a nested type context.
Set<Typedef<T>> get getNestedTDart => throw UnimplementedError(); JSList<Typedef<T>> get getNestedTDart => throw UnimplementedError();
external Set<Typedef<T>> get getNestedT; // No error as JSList is an interop type.
// ^ external JSList<Typedef<T>> 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 JSList<Typedef<U>> get getNestedU;
external Set<Typedef<U>> get getNestedU; external JSList<Typedef<V>> get getNestedV;
external Set<Typedef<V>> get getNestedV;
// Test type parameters that are declared by the member. // Test type parameters that are declared by the member.
W returnWDart<W>() => throw UnimplementedError(); W returnWDart<W>() => throw UnimplementedError();
@ -109,15 +112,15 @@ extension PkgJsExtension<T> on PkgJs<T> {
@JS() @JS()
@staticInterop @staticInterop
class Instantiated { class Instantiated {
external factory Instantiated(List<JSNumber> list); external factory Instantiated(JSList<JSNumber> list);
} }
extension InstantiatedExtension on Instantiated { extension InstantiatedExtension on Instantiated {
external List<Instantiated> fieldList; external JSList<int> fieldList;
external List<Instantiated> get getList; external JSList<int> get getList;
external set setList(List<Instantiated> list); external set setList(JSList<int> list);
external List<Instantiated> returnList(); external JSList<int> returnList();
external void consumeList(List<Instantiated> list); external void consumeList(JSList<int> list);
} }
void main() {} void main() {}

View file

@ -11,7 +11,7 @@ import 'package:expect/minitest.dart';
import 'package:js/js.dart' show staticInterop; import 'package:js/js.dart' show staticInterop;
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS() @JS()
@staticInterop @staticInterop

View file

@ -11,7 +11,7 @@ import 'package:expect/minitest.dart';
import 'package:js/js.dart' show trustTypes, staticInterop; import 'package:js/js.dart' show trustTypes, staticInterop;
@JS() @JS()
external dynamic eval(String code); external void eval(String code);
@JS('ExternalStatic') @JS('ExternalStatic')
@staticInterop @staticInterop