1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

[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`
- **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

View File

@ -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'(?<!generated_)tests/lib/js/static_interop_test/strict_mode_test.dart'));
}
void _checkDisallowedLibrariesInStrictMode(Library node) {
void _checkDisallowedLibrariesForDart2Wasm(Library node) {
for (final dependency in node.dependencies) {
final dependencyUriString = dependency.targetLibrary.importUri.toString();
if (_disallowedLibrariesInStrictMode.contains(dependencyUriString)) {
@ -735,9 +718,9 @@ class JsInteropChecks extends RecursiveVisitor {
node.name.text.length,
node.location?.file);
} else {
_reportStaticInvocationIfNotJSType(functionType.returnType, node);
_reportStaticInvocationIfNotAllowedType(functionType.returnType, node);
for (final parameter in functionType.positionalParameters) {
_reportStaticInvocationIfNotJSType(parameter, node);
_reportStaticInvocationIfNotAllowedType(parameter, node);
}
}
}
@ -946,8 +929,7 @@ class JsInteropChecks extends RecursiveVisitor {
return false;
}
void _reportIfNotJSType(
DartType type, TreeNode node, Name name, Uri? fileUri) {
bool _isAllowedExternalType(DartType type) {
// TODO(joshualitt): We allow only JS types on external JS interop APIs with
// two exceptions: `void` and `Null`. Both of these exceptions exist largely
// 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
// provide JS types equivalents, but likely only if we have implicit
// conversions between Dart types and JS types.
if (_enforceStrictMode &&
!(type is VoidType ||
type is NullType ||
(type is InterfaceType &&
hasStaticInteropAnnotation(type.classNode)) ||
(type is ExtensionType &&
_extensionIndex
.isInteropExtensionType(type.extensionTypeDeclaration)))) {
// Type parameter types are checked elsewhere.
if (type is VoidType || type is NullType || type is TypeParameterType) {
return true;
}
if (type is InterfaceType) {
final cls = type.classNode;
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(
templateJsInteropStrictModeViolation.withArguments(type, true),
templateJsInteropStaticInteropExternalTypeViolation.withArguments(
type, true),
node.fileOffset,
name.text.length,
fileUri);
}
}
void _reportProcedureIfNotJSType(DartType type, Procedure node) =>
_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

View File

@ -166,7 +166,7 @@ class WasmTarget extends Target {
diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>);
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);

View File

@ -4108,6 +4108,38 @@ Message _withArgumentsJsInteropFunctionToJSRequiresStaticType(
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.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
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<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.
const Template<
Message Function(

View File

@ -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

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."
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."

View File

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

View File

@ -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<T extends JSAny?, U extends Nested> {
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]);
}
@JS('module.Bar')
@staticInterop
@pkgJs.JS('module.Bar')
@pkgJs.staticInterop
class Bar {
external factory Bar(int a);
}

View File

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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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<T extends JSAny?, U extends Nested>._(JSObject _) {
external External();

View File

@ -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) {

View File

@ -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) {

View File

@ -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.

View File

@ -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('''

View File

@ -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.

View File

@ -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 ~();

View File

@ -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<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);
// ^
// [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<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);
// ^
// [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<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);
// ^

View File

@ -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();

View File

@ -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.

View File

@ -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<T extends JSAny, U extends Nested> {
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]);
}
@JS('module.Bar')
@staticInterop
@pkgJs.JS('module.Bar')
@pkgJs.staticInterop
class Bar {
external factory Bar(int a);
}

View File

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

View File

@ -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

View File

@ -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