[package:js] Add @trustTypes annotation.

Adds an experimental `@trustTypes` annotation which can be used during
migration to static interop to preserve the older semantics for JS interop.

Change-Id: Ic00a6c968b15f8c8f5d0840b82db5a6670eaf0eb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241362
Reviewed-by: Riley Porter <rileyporter@google.com>
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Joshua Litt 2022-04-20 15:24:18 +00:00 committed by Commit Bot
parent af2de233e5
commit 84497e41ca
24 changed files with 439 additions and 22 deletions

View file

@ -7022,6 +7022,67 @@ const MessageCode messageJsInteropOperatorsNotSupported = const MessageCode(
problemMessage: r"""JS interop classes do not support operator methods.""",
correctionMessage: r"""Try replacing this with a normal method.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)>
templateJsInteropStaticInteropTrustTypesUsageNotAllowed =
const Template<Message Function(String name)>(
problemMessageTemplate:
r"""JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""",
correctionMessageTemplate:
r"""Try removing the `@trustTypes` annotation.""",
withArguments:
_withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(String name)>
codeJsInteropStaticInteropTrustTypesUsageNotAllowed =
const Code<Message Function(String name)>(
"JsInteropStaticInteropTrustTypesUsageNotAllowed",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed(
String name) {
if (name.isEmpty) throw 'No name provided';
name = demangleMixinApplicationName(name);
return new Message(codeJsInteropStaticInteropTrustTypesUsageNotAllowed,
problemMessage:
"""JS interop class '${name}' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""",
correctionMessage: """Try removing the `@trustTypes` annotation.""",
arguments: {'name': name});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)>
templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop =
const Template<Message Function(String name)>(
problemMessageTemplate:
r"""JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""",
correctionMessageTemplate:
r"""Try marking the class using `@staticInterop`.""",
withArguments:
_withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(String name)>
codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop =
const Code<Message Function(String name)>(
"JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop(
String name) {
if (name.isEmpty) throw 'No name provided';
name = demangleMixinApplicationName(name);
return new Message(
codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop,
problemMessage:
"""JS interop class '${name}' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""",
correctionMessage: """Try marking the class using `@staticInterop`.""",
arguments: {'name': name});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)>
templateJsInteropStaticInteropWithInstanceMembers =

View file

@ -22,7 +22,9 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
templateJsInteropStaticInteropWithInstanceMembers,
templateJsInteropStaticInteropWithNonStaticSupertype,
templateJsInteropJSClassExtendsDartClass,
templateJsInteropNativeClassInAnnotation;
templateJsInteropNativeClassInAnnotation,
templateJsInteropStaticInteropTrustTypesUsageNotAllowed,
templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop;
import 'src/js_interop.dart';
@ -67,6 +69,11 @@ class JsInteropChecks extends RecursiveVisitor {
'generated_tests/web_2/native/native_test',
];
List<Pattern> _allowedTrustTypesTestPatterns = [
RegExp(r'(?<!generated_)tests/lib/js'),
RegExp(r'(?<!generated_)tests/lib_2/js'),
];
bool _libraryIsGlobalNamespace = false;
JsInteropChecks(
@ -104,6 +111,25 @@ class JsInteropChecks extends RecursiveVisitor {
_classHasJSAnnotation = hasJSInteropAnnotation(cls);
_classHasAnonymousAnnotation = hasAnonymousAnnotation(cls);
_classHasStaticInteropAnnotation = hasStaticInteropAnnotation(cls);
bool classHasTrustTypesAnnotation = hasTrustTypesAnnotation(cls);
if (classHasTrustTypesAnnotation) {
if (!_isAllowedTrustTypesUsage(cls)) {
_diagnosticsReporter.report(
templateJsInteropStaticInteropTrustTypesUsageNotAllowed
.withArguments(cls.name),
cls.fileOffset,
cls.name.length,
cls.fileUri);
}
if (!_classHasStaticInteropAnnotation) {
_diagnosticsReporter.report(
templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop
.withArguments(cls.name),
cls.fileOffset,
cls.name.length,
cls.fileUri);
}
}
var superclass = cls.superclass;
if (superclass != null && superclass != _coreTypes.objectClass) {
var superHasJSAnnotation = hasJSInteropAnnotation(superclass);
@ -357,6 +383,14 @@ class JsInteropChecks extends RecursiveVisitor {
}
}
/// Verifies that use of `@trustTypes` is allowed.
bool _isAllowedTrustTypesUsage(Class cls) {
Uri uri = cls.enclosingLibrary.importUri;
return uri.isScheme('dart') && uri.path == 'ui' ||
_allowedTrustTypesTestPatterns
.any((pattern) => uri.path.contains(pattern));
}
/// Verifies given member is one of the allowed usages of external:
/// a dart low level library, a foreign helper, a native test,
/// or a from environment constructor.

View file

@ -20,6 +20,11 @@ bool hasAnonymousAnnotation(Annotatable a) =>
bool hasStaticInteropAnnotation(Annotatable a) =>
a.annotations.any(_isStaticInteropAnnotation);
/// Returns true iff the node has an `@trustTypes` annotation from
/// `package:js` or from the internal `dart:_js_annotations`.
bool hasTrustTypesAnnotation(Annotatable a) =>
a.annotations.any(_isTrustTypesAnnotation);
/// If [a] has a `@JS('...')` annotation, returns the value inside the
/// parentheses.
///
@ -80,6 +85,9 @@ bool _isAnonymousAnnotation(Expression value) =>
bool _isStaticInteropAnnotation(Expression value) =>
_isInteropAnnotation(value, '_StaticInterop');
bool _isTrustTypesAnnotation(Expression value) =>
_isInteropAnnotation(value, '_TrustTypes');
/// Returns true if [value] is the `Native` annotation from `dart:_js_helper`.
bool _isNativeAnnotation(Expression value) {
var c = _annotationClass(value);

View file

@ -7,16 +7,19 @@ import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/type_environment.dart';
import '../js_interop.dart' show getJSName;
import '../js_interop.dart' show getJSName, hasTrustTypesAnnotation;
/// Replaces js_util methods with inline calls to foreign_helper JS which
/// emits the code as a JavaScript code fragment.
class JsUtilOptimizer extends Transformer {
final Procedure _callMethodTarget;
final Procedure _callMethodTrustTypeTarget;
final List<Procedure> _callMethodUncheckedTargets;
final List<Procedure> _callMethodUncheckedTrustTypeTargets;
final Procedure _callConstructorTarget;
final List<Procedure> _callConstructorUncheckedTargets;
final Procedure _getPropertyTarget;
final Procedure _getPropertyTrustTypeTarget;
final Procedure _setPropertyTarget;
final Procedure _setPropertyUncheckedTarget;
@ -37,14 +40,21 @@ class JsUtilOptimizer extends Transformer {
final CoreTypes _coreTypes;
final StatefulStaticTypeContext _staticTypeContext;
Map<Reference, ExtensionMemberDescriptor>? _extensionMemberIndex;
late Set<Reference> _shouldTrustType;
JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
: _callMethodTarget =
_coreTypes.index.getTopLevelProcedure('dart:js_util', 'callMethod'),
_callMethodTrustTypeTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_callMethodTrustType'),
_callMethodUncheckedTargets = List<Procedure>.generate(
5,
(i) => _coreTypes.index.getTopLevelProcedure(
'dart:js_util', '_callMethodUnchecked$i')),
_callMethodUncheckedTrustTypeTargets = List<Procedure>.generate(
5,
(i) => _coreTypes.index.getTopLevelProcedure(
'dart:js_util', '_callMethodUncheckedTrustType$i')),
_callConstructorTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'callConstructor'),
_callConstructorUncheckedTargets = List<Procedure>.generate(
@ -53,6 +63,8 @@ class JsUtilOptimizer extends Transformer {
'dart:js_util', '_callConstructorUnchecked$i')),
_getPropertyTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'getProperty'),
_getPropertyTrustTypeTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', '_getPropertyTrustType'),
_setPropertyTarget = _coreTypes.index
.getTopLevelProcedure('dart:js_util', 'setProperty'),
_setPropertyUncheckedTarget = _coreTypes.index
@ -93,14 +105,16 @@ class JsUtilOptimizer extends Transformer {
if (node.isExternal && node.isExtensionMember) {
var index = _extensionMemberIndex ??=
_createExtensionMembersIndex(node.enclosingLibrary);
var nodeDescriptor = index[node.reference]!;
Reference reference = node.reference;
var nodeDescriptor = index[reference]!;
bool shouldTrustType = _shouldTrustType.contains(reference);
if (!nodeDescriptor.isStatic) {
if (nodeDescriptor.kind == ExtensionMemberKind.Getter) {
transformedBody = _getExternalGetterBody(node);
transformedBody = _getExternalGetterBody(node, shouldTrustType);
} else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) {
transformedBody = _getExternalSetterBody(node);
} else if (nodeDescriptor.kind == ExtensionMemberKind.Method) {
transformedBody = _getExternalMethodBody(node);
transformedBody = _getExternalMethodBody(node, shouldTrustType);
}
}
}
@ -120,9 +134,17 @@ class JsUtilOptimizer extends Transformer {
Map<Reference, ExtensionMemberDescriptor> _createExtensionMembersIndex(
Library library) {
_extensionMemberIndex = {};
library.extensions.forEach((extension) => extension.members.forEach(
(descriptor) =>
_extensionMemberIndex![descriptor.member] = descriptor));
_shouldTrustType = {};
library.extensions
.forEach((extension) => extension.members.forEach((descriptor) {
Reference reference = descriptor.member;
_extensionMemberIndex![reference] = descriptor;
DartType onType = extension.onType;
if (onType is InterfaceType &&
hasTrustTypesAnnotation(onType.className.asClass)) {
_shouldTrustType.add(reference);
}
}));
return _extensionMemberIndex!;
}
@ -130,11 +152,13 @@ class JsUtilOptimizer extends Transformer {
///
/// The new function body will call the optimized version of
/// `js_util.getProperty` for the given external getter.
ReturnStatement _getExternalGetterBody(Procedure node) {
ReturnStatement _getExternalGetterBody(Procedure node, bool shouldTrustType) {
var function = node.function;
assert(function.positionalParameters.length == 1);
Procedure target =
shouldTrustType ? _getPropertyTrustTypeTarget : _getPropertyTarget;
var getPropertyInvocation = StaticInvocation(
_getPropertyTarget,
target,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getExtensionMemberName(node))
@ -170,10 +194,12 @@ class JsUtilOptimizer extends Transformer {
///
/// The new function body will call the optimized version of
/// `js_util.callMethod` for the given external method.
ReturnStatement _getExternalMethodBody(Procedure node) {
ReturnStatement _getExternalMethodBody(Procedure node, bool shouldTrustType) {
var function = node.function;
Procedure target =
shouldTrustType ? _callMethodTrustTypeTarget : _callMethodTarget;
var callMethodInvocation = StaticInvocation(
_callMethodTarget,
target,
Arguments([
VariableGet(function.positionalParameters.first),
StringLiteral(_getExtensionMemberName(node)),
@ -185,7 +211,8 @@ class JsUtilOptimizer extends Transformer {
function.returnType
]))
..fileOffset = node.fileOffset;
return ReturnStatement(_lowerCallMethod(callMethodInvocation));
return ReturnStatement(_lowerCallMethod(callMethodInvocation,
shouldTrustType: shouldTrustType));
}
/// Returns the extension member name.
@ -214,7 +241,8 @@ class JsUtilOptimizer extends Transformer {
if (node.target == _setPropertyTarget) {
node = _lowerSetProperty(node);
} else if (node.target == _callMethodTarget) {
node = _lowerCallMethod(node);
// Never trust types on explicit `js_util` calls.
node = _lowerCallMethod(node, shouldTrustType: false);
} else if (node.target == _callConstructorTarget) {
node = _lowerCallConstructor(node);
}
@ -245,13 +273,17 @@ class JsUtilOptimizer extends Transformer {
/// Calls will be lowered when using a List literal or constant list with 0-4
/// elements for the `callMethod` arguments, or the `List.empty()` factory.
/// Removing the checks allows further inlining by the compilers.
StaticInvocation _lowerCallMethod(StaticInvocation node) {
StaticInvocation _lowerCallMethod(StaticInvocation node,
{required bool shouldTrustType}) {
Arguments arguments = node.arguments;
assert(arguments.positional.length == 3);
assert(arguments.named.isEmpty);
List<Procedure> targets = shouldTrustType
? _callMethodUncheckedTrustTypeTargets
: _callMethodUncheckedTargets;
return _lowerToCallUnchecked(
node, _callMethodUncheckedTargets, arguments.positional.sublist(0, 2));
node, targets, arguments.positional.sublist(0, 2));
}
/// Lowers the given js_util `callConstructor` call to `_callConstructorUncheckedN`

View file

@ -577,6 +577,10 @@ JsInteropStaticInteropWithInstanceMembers/analyzerCode: Fail # Web compiler spec
JsInteropStaticInteropWithInstanceMembers/example: Fail # Web compiler specific
JsInteropStaticInteropWithNonStaticSupertype/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropWithNonStaticSupertype/example: Fail # Web compiler specific
JsInteropStaticInteropTrustTypesUsageNotAllowed/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropTrustTypesUsageNotAllowed/example: Fail # Web compiler specific
JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/example: Fail # Web compiler specific
LanguageVersionInvalidInDotPackages/analyzerCode: Fail
LanguageVersionMismatchInPart/analyzerCode: Fail
LanguageVersionMismatchInPart/part_wrapped_script: Fail # Part in (now) part.

View file

@ -5197,6 +5197,14 @@ JsInteropStaticInteropWithNonStaticSupertype:
problemMessage: "JS interop class '#name' has an `@staticInterop` annotation, but has supertype '#name2', which is non-static."
correctionMessage: "Try marking the supertype as a static interop class using `@staticInterop`."
JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop:
problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation."
correctionMessage: "Try marking the class using `@staticInterop`."
JsInteropStaticInteropTrustTypesUsageNotAllowed:
problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk."
correctionMessage: "Try removing the `@trustTypes` annotation."
DefaultListConstructorError:
problemMessage: "Can't use the default List constructor."
correctionMessage: "Try using List.filled instead."

View file

@ -87,6 +87,7 @@ t
team
this.namedconstructor
this.x
trusttypes
type3.#name
u
unsound

View file

@ -35,5 +35,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -35,5 +35,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -35,5 +35,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -37,5 +37,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123b.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -37,5 +37,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123b.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -37,5 +37,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///issue46123b.dart:
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9)
- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9)
- Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)

View file

@ -5,6 +5,8 @@
/// Annotations to mark interfaces to JavaScript.
library js;
import 'package:meta/meta.dart';
export 'dart:js' show allowInterop, allowInteropCaptureThis;
/// An annotation that indicates a library, class, or member is implemented
@ -45,3 +47,16 @@ const _Anonymous anonymous = _Anonymous();
/// These classes should not contain any instance members, inherited or
/// otherwise, and should instead use static extension members.
const _StaticInterop staticInterop = _StaticInterop();
/// NOTE: [trustTypes] is an experimental annotation that may disappear at any
/// point in time. It exists solely to help users who wish to migrate classes
/// from the older style of JS interop to the new static interop model but wish
/// to preserve the older semantics for type checks. This annotation must be
/// used alongside [staticInterop] and it affects any external methods in any
/// extension to the static interop class.
@experimental
class _TrustTypes {
const _TrustTypes();
}
const _TrustTypes trustTypes = _TrustTypes();

View file

@ -6,5 +6,8 @@ repository: https://github.com/dart-lang/sdk/tree/main/pkg/js
environment:
sdk: ">=2.16.0-100.0.dev <3.0.0"
dependencies:
meta: ^1.7.0
dev_dependencies:
lints: any

View file

@ -74,6 +74,10 @@ bool hasProperty(Object o, Object name) => JS('bool', '# in #', name, o);
T getProperty<T>(Object o, Object name) =>
JS<dynamic>('Object|Null', '#[#]', o, name);
/// Similar to [getProperty] but introduces an unsound implicit cast to `T`.
T _getPropertyTrustType<T>(Object o, Object name) =>
JS<T>('Object|Null', '#[#]', o, name);
// A CFE transformation may optimize calls to `setProperty`, when [value] is
// statically known to be a non-function.
T setProperty<T>(Object o, Object name, T? value) {
@ -95,18 +99,38 @@ T callMethod<T>(Object o, String method, List<Object?> args) {
return JS<dynamic>('Object|Null', '#[#].apply(#, #)', o, method, o, args);
}
/// Similar to [callMethod] but introduces an unsound implicit cast to `T`.
T _callMethodTrustType<T>(Object o, String method, List<Object?> args) {
assertInteropArgs(args);
return JS<T>('Object|Null', '#[#].apply(#, #)', o, method, o, args);
}
/// Unchecked version for 0 arguments, only used in a CFE transformation.
@pragma('dart2js:tryInline')
T _callMethodUnchecked0<T>(Object o, String method) {
return JS<dynamic>('Object|Null', '#[#]()', o, method);
}
/// Similar to [_callMethodUnchecked] but introduces an unsound implicit cast
/// to `T`.
@pragma('dart2js:tryInline')
T _callMethodUncheckedTrustType0<T>(Object o, String method) {
return JS<T>('Object|Null', '#[#]()', o, method);
}
/// Unchecked version for 1 argument, only used in a CFE transformation.
@pragma('dart2js:tryInline')
T _callMethodUnchecked1<T>(Object o, String method, Object? arg1) {
return JS<dynamic>('Object|Null', '#[#](#)', o, method, arg1);
}
/// Similar to [_callMethodUnchecked1] but introduces an unsound implicit cast
/// to `T`.
@pragma('dart2js:tryInline')
T _callMethodUncheckedTrustType1<T>(Object o, String method, Object? arg1) {
return JS<T>('Object|Null', '#[#](#)', o, method, arg1);
}
/// Unchecked version for 2 arguments, only used in a CFE transformation.
@pragma('dart2js:tryInline')
T _callMethodUnchecked2<T>(
@ -114,6 +138,14 @@ T _callMethodUnchecked2<T>(
return JS<dynamic>('Object|Null', '#[#](#, #)', o, method, arg1, arg2);
}
/// Similar to [_callMethodUnchecked2] but introduces an unsound implicit cast
/// to `T`.
@pragma('dart2js:tryInline')
T _callMethodUncheckedTrustType2<T>(
Object o, String method, Object? arg1, Object? arg2) {
return JS<T>('Object|Null', '#[#](#, #)', o, method, arg1, arg2);
}
/// Unchecked version for 3 arguments, only used in a CFE transformation.
@pragma('dart2js:tryInline')
T _callMethodUnchecked3<T>(
@ -122,6 +154,14 @@ T _callMethodUnchecked3<T>(
'Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3);
}
/// Similar to [_callMethodUnchecked3] but introduces an unsound implicit cast
/// to `T`.
@pragma('dart2js:tryInline')
T _callMethodUncheckedTrustType3<T>(
Object o, String method, Object? arg1, Object? arg2, Object? arg3) {
return JS<T>('Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3);
}
/// Unchecked version for 4 arguments, only used in a CFE transformation.
@pragma('dart2js:tryInline')
T _callMethodUnchecked4<T>(Object o, String method, Object? arg1, Object? arg2,
@ -130,6 +170,15 @@ T _callMethodUnchecked4<T>(Object o, String method, Object? arg1, Object? arg2,
'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4);
}
/// Similar to [_callMethodUnchecked4] but introduces an unsound implicit cast
/// to `T`.
@pragma('dart2js:tryInline')
T _callMethodUncheckedTrustType4<T>(Object o, String method, Object? arg1,
Object? arg2, Object? arg3, Object? arg4) {
return JS<T>(
'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4);
}
/// Check whether [o] is an instance of [type].
///
/// The value in [type] is expected to be a JS-interop object that

View file

@ -0,0 +1,11 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:js/js.dart';
@JS()
@trustTypes
class Incorrect {}
// ^
// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.

View file

@ -0,0 +1,77 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Test that the [trustTypes] annotation has the same semantics as the older
// style of js interop.
import 'package:js/js.dart';
import 'package:expect/expect.dart';
class NonExistent {}
@JS()
@staticInterop
@trustTypes
class TrustMe {
external factory TrustMe();
}
extension TrustMeExtension on TrustMe {
external List<NonExistent> get foos;
external List<NonExistent> callFoos();
external List<NonExistent> callFoos1(int ignored);
external List<NonExistent> callFoos2(int ignored1, int ignored2);
external List<NonExistent> callFoos3(
int ignored1, int ignored2, int ignored3);
external List<NonExistent> callFoos4(
int ignored1, int ignored2, int ignored3, int ignored4);
external List<NonExistent> callFoos5(
int ignored1, int ignored2, int ignored3, int ignored4, int ignored5);
external String get fooPrimitive;
}
@JS()
external void eval(String code);
void main() {
eval(r'''
TrustMe = function TrustMe() {
this.foos = 'not a list 1';
this.fooPrimitive = 5;
this.callFoos = function() {
return 'not a list 2';
}
this.callFoos1 = function(a) {
return 'not a list 3';
}
this.callFoos2 = function(a, b) {
return 'not a list 4';
}
this.callFoos3 = function(a, b, c) {
return 'not a list 5';
}
this.callFoos4 = function(a, b, c, d) {
return 'not a list 6';
}
this.callFoos5 = function(a, b, c, d, e) {
return 'not a list 7';
}
}
''');
final trusted = TrustMe();
Expect.equals('not a list 1', trusted.foos.toString());
Expect.equals('not a list 2', trusted.callFoos().toString());
Expect.equals('not a list 3', trusted.callFoos1(1).toString());
Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString());
Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString());
Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString());
final falseList = trusted.callFoos5(1, 1, 1, 1, 1);
Expect.equals('not a list 7', falseList.toString());
Expect.throws(() => falseList.removeAt(0));
final falseString = trusted.fooPrimitive;
Expect.equals(5, falseString);
Expect.throws(() => falseString.codeUnitAt(0));
}

View file

@ -0,0 +1,11 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:js/js.dart';
@JS()
@trustTypes
class Incorrect {}
// ^
// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.

View file

@ -0,0 +1,77 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Test that the [trustTypes] annotation has the same semantics as the older
// style of js interop.
import 'package:js/js.dart';
import 'package:expect/expect.dart';
class NonExistent {}
@JS()
@staticInterop
@trustTypes
class TrustMe {
external factory TrustMe();
}
extension TrustMeExtension on TrustMe {
external List<NonExistent> get foos;
external List<NonExistent> callFoos();
external List<NonExistent> callFoos1(int ignored);
external List<NonExistent> callFoos2(int ignored1, int ignored2);
external List<NonExistent> callFoos3(
int ignored1, int ignored2, int ignored3);
external List<NonExistent> callFoos4(
int ignored1, int ignored2, int ignored3, int ignored4);
external List<NonExistent> callFoos5(
int ignored1, int ignored2, int ignored3, int ignored4, int ignored5);
external String get fooPrimitive;
}
@JS()
external void eval(String code);
void main() {
eval(r'''
TrustMe = function TrustMe() {
this.foos = 'not a list 1';
this.fooPrimitive = 5;
this.callFoos = function() {
return 'not a list 2';
}
this.callFoos1 = function(a) {
return 'not a list 3';
}
this.callFoos2 = function(a, b) {
return 'not a list 4';
}
this.callFoos3 = function(a, b, c) {
return 'not a list 5';
}
this.callFoos4 = function(a, b, c, d) {
return 'not a list 6';
}
this.callFoos5 = function(a, b, c, d, e) {
return 'not a list 7';
}
}
''');
final trusted = TrustMe();
Expect.equals('not a list 1', trusted.foos.toString());
Expect.equals('not a list 2', trusted.callFoos().toString());
Expect.equals('not a list 3', trusted.callFoos1(1).toString());
Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString());
Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString());
Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString());
final falseList = trusted.callFoos5(1, 1, 1, 1, 1);
Expect.equals('not a list 7', falseList.toString());
Expect.throws(() => falseList.removeAt(0));
final falseString = trusted.fooPrimitive;
Expect.equals(5, falseString);
Expect.throws(() => falseString.codeUnitAt(0));
}

View file

@ -6,5 +6,6 @@
dependencies:
main: log
log: js
js: meta
packages:
js: ../../../pkg/js/lib

View file

@ -7,5 +7,6 @@
dependencies:
main: static_interop
static_interop: js
js: meta
packages:
js: ../../../pkg/js/lib

View file

@ -0,0 +1,12 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:js/js.dart';
@JS()
@staticInterop
@trustTypes
class Incorrect {}
// ^
// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.

View file

@ -0,0 +1,12 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:js/js.dart';
@JS()
@staticInterop
@trustTypes
class Incorrect {}
// ^
// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.