mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:31:50 +00:00
[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:
parent
af2de233e5
commit
84497e41ca
|
@ -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 =
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -87,6 +87,7 @@ t
|
|||
team
|
||||
this.namedconstructor
|
||||
this.x
|
||||
trusttypes
|
||||
type3.#name
|
||||
u
|
||||
unsound
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
11
tests/lib/js/trust_types_annotation_test.dart
Normal file
11
tests/lib/js/trust_types_annotation_test.dart
Normal 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.
|
77
tests/lib/js/trust_types_test.dart
Normal file
77
tests/lib/js/trust_types_test.dart
Normal 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));
|
||||
}
|
11
tests/lib_2/js/trust_types_annotation_test.dart
Normal file
11
tests/lib_2/js/trust_types_annotation_test.dart
Normal 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.
|
77
tests/lib_2/js/trust_types_test.dart
Normal file
77
tests/lib_2/js/trust_types_test.dart
Normal 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));
|
||||
}
|
|
@ -6,5 +6,6 @@
|
|||
dependencies:
|
||||
main: log
|
||||
log: js
|
||||
js: meta
|
||||
packages:
|
||||
js: ../../../pkg/js/lib
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
dependencies:
|
||||
main: static_interop
|
||||
static_interop: js
|
||||
js: meta
|
||||
packages:
|
||||
js: ../../../pkg/js/lib
|
||||
|
|
12
tests/web/js_trust_types_disallowed_test.dart
Normal file
12
tests/web/js_trust_types_disallowed_test.dart
Normal 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.
|
12
tests/web_2/js_trust_types_disallowed_test.dart
Normal file
12
tests/web_2/js_trust_types_disallowed_test.dart
Normal 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.
|
Loading…
Reference in a new issue