[dart:js_interop] Add isA helper

Closes https://github.com/dart-lang/sdk/issues/54138

Adds a helper to do better type-checks so that users don't
accidentally using is checks or have to manually do the right
typeof or instanceof checks. In order to do this, there is
some refactoring to make ExportCreator a SharedInteropTransformer
(as it's shared across all backends) so that we can reuse an
existing visitor. In the same class, we remove unnecessary setting
of parent pointers. We should clean up the fileOffsets as well,
but dart2js verifies that those are set, so we keep them as is
for now. Also adds some static errors for edge cases.

CoreLibraryReviewExempt: Helper for web-specific library.
Change-Id: I34d818ada1349b69afd15d170d3fafa0460f65fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347225
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
Srujan Gaddam 2024-01-25 18:52:45 +00:00 committed by Commit Queue
parent 8c7ccfafd2
commit 3b294fdab2
39 changed files with 2429 additions and 442 deletions

View file

@ -20,6 +20,10 @@
opaque Dart value instead of only externalizing the value. Like the JS
backends, you'll now get a more useful error when trying to use it in another
Dart runtime.
- Added `isA` helper to make type checks easier with interop types. See
[#54138][] for more details.
[#54138]: https://github.com/dart-lang/sdk/issues/54138
## 3.3.0

View file

@ -8857,6 +8857,16 @@ const MessageCode messageJsInteropInvalidStaticClassMemberName = const MessageCo
problemMessage:
r"""JS interop static class members cannot have '.' in their JS name.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeJsInteropIsATearoff = messageJsInteropIsATearoff;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageJsInteropIsATearoff = const MessageCode(
"JsInteropIsATearoff",
problemMessage: r"""'isA' can't be torn off.""",
correctionMessage:
r"""Use a method that calls 'isA' and tear off that method instead.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(

View file

@ -60,7 +60,7 @@ import 'src/js_interop.dart';
class JsInteropChecks extends RecursiveVisitor {
final Set<Constant> _constantCache = {};
final CoreTypes _coreTypes;
late final ExtensionIndex _extensionIndex;
late final ExtensionIndex extensionIndex;
final Procedure _functionToJSTarget;
// Errors on constants need source information, so we use the surrounding
// `ConstantExpression` as the source.
@ -163,7 +163,7 @@ class JsInteropChecks extends RecursiveVisitor {
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
_staticTypeContext = StatefulStaticTypeContext.stacked(
TypeEnvironment(_coreTypes, hierarchy)) {
_extensionIndex =
extensionIndex =
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment);
}
@ -209,7 +209,7 @@ class JsInteropChecks extends RecursiveVisitor {
node.fileOffset, node.name.length, node.fileUri);
}
if (hasDartJSInteropAnnotation(node) &&
!_extensionIndex.isInteropExtensionType(node)) {
!extensionIndex.isInteropExtensionType(node)) {
_reporter.report(
templateJsInteropExtensionTypeNotInterop.withArguments(
node.name, node.declaredRepresentationType, true),
@ -352,9 +352,9 @@ class JsInteropChecks extends RecursiveVisitor {
// can only have named parameters, and every other interop member can only
// have positional parameters.
final isObjectLiteralConstructor = node.isExtensionTypeMember &&
(_extensionIndex.getExtensionTypeDescriptor(node)!.kind ==
(extensionIndex.getExtensionTypeDescriptor(node)!.kind ==
ExtensionTypeMemberKind.Constructor ||
_extensionIndex.getExtensionTypeDescriptor(node)!.kind ==
extensionIndex.getExtensionTypeDescriptor(node)!.kind ==
ExtensionTypeMemberKind.Factory) &&
node.function.namedParameters.isNotEmpty;
final isAnonymousFactory = _classHasAnonymousAnnotation && node.isFactory;
@ -381,13 +381,13 @@ class JsInteropChecks extends RecursiveVisitor {
_checkNoParamInitializersForStaticInterop(node.function);
late Annotatable? annotatable;
if (node.isExtensionTypeMember) {
annotatable = _extensionIndex.getExtensionType(node);
annotatable = extensionIndex.getExtensionType(node);
} else if (node.isExtensionMember) {
annotatable = _extensionIndex.getExtensionAnnotatable(node);
annotatable = extensionIndex.getExtensionAnnotatable(node);
if (annotatable != null) {
// We do not support external extension members with the 'static'
// keyword currently.
if (_extensionIndex.getExtensionDescriptor(node)!.isStatic) {
if (extensionIndex.getExtensionDescriptor(node)!.isStatic) {
report(
messageJsInteropExternalExtensionMemberWithStaticDisallowed);
}
@ -644,13 +644,13 @@ class JsInteropChecks extends RecursiveVisitor {
if (member.isExternal) {
if (_isAllowedExternalUsage(member)) return;
if (member.isExtensionMember) {
final annotatable = _extensionIndex.getExtensionAnnotatable(member);
final annotatable = extensionIndex.getExtensionAnnotatable(member);
if (annotatable == null) {
_reporter.report(messageJsInteropExternalExtensionMemberOnTypeInvalid,
member.fileOffset, member.name.text.length, member.fileUri);
}
} else if (member.isExtensionTypeMember) {
final extensionType = _extensionIndex.getExtensionType(member);
final extensionType = extensionIndex.getExtensionType(member);
if (extensionType == null) {
_reporter.report(messageJsInteropExtensionTypeMemberNotInterop,
member.fileOffset, member.name.text.length, member.fileUri);
@ -680,25 +680,27 @@ class JsInteropChecks extends RecursiveVisitor {
/// Returns whether an error was triggered.
bool _checkDisallowedTearoff(Member member, TreeNode? context) {
if (context == null || context.location == null) return false;
if (member.isExternal) {
// TODO(srujzs): Delete the check for patched member once
// https://github.com/dart-lang/sdk/issues/53367 is resolved.
if (member.isExternal && !JsInteropChecks.isPatchedMember(member)) {
var memberKind = '';
var memberName = '';
if (member.isExtensionTypeMember) {
// Extension type interop members can not be torn off.
if (_extensionIndex.getExtensionType(member) == null) {
if (extensionIndex.getExtensionType(member) == null) {
return false;
}
memberKind = 'extension type interop member';
memberName =
_extensionIndex.getExtensionTypeDescriptor(member)!.name.text;
extensionIndex.getExtensionTypeDescriptor(member)!.name.text;
if (memberName.isEmpty) memberName = 'new';
} else if (member.isExtensionMember) {
// JS interop members can not be torn off.
if (_extensionIndex.getExtensionAnnotatable(member) == null) {
if (extensionIndex.getExtensionAnnotatable(member) == null) {
return false;
}
memberKind = 'extension interop member';
memberName = _extensionIndex.getExtensionDescriptor(member)!.name.text;
memberName = extensionIndex.getExtensionDescriptor(member)!.name.text;
} else if (member.enclosingClass != null) {
// @staticInterop members can not be torn off.
final enclosingClass = member.enclosingClass!;
@ -783,14 +785,14 @@ class JsInteropChecks extends RecursiveVisitor {
var isInvalidOperator = false;
var operatorHasRenaming = false;
if ((node.isExtensionTypeMember &&
_extensionIndex.getExtensionTypeDescriptor(node)?.kind ==
extensionIndex.getExtensionTypeDescriptor(node)?.kind ==
ExtensionTypeMemberKind.Operator) ||
(node.isExtensionMember &&
_extensionIndex.getExtensionDescriptor(node)?.kind ==
extensionIndex.getExtensionDescriptor(node)?.kind ==
ExtensionMemberKind.Operator)) {
final operator =
_extensionIndex.getExtensionTypeDescriptor(node)?.name.text ??
_extensionIndex.getExtensionDescriptor(node)?.name.text;
extensionIndex.getExtensionTypeDescriptor(node)?.name.text ??
extensionIndex.getExtensionDescriptor(node)?.name.text;
isInvalidOperator = operator != '[]' && operator != '[]=';
operatorHasRenaming = getJSName(node).isNotEmpty;
} else if (!node.isStatic && node.kind == ProcedureKind.Operator) {
@ -858,8 +860,8 @@ class JsInteropChecks extends RecursiveVisitor {
/// Otherwise, return null.
Member? _getTornOffFromGeneratedTearOff(Procedure procedure) {
final tornOff =
_extensionIndex.getExtensionTypeMemberForTearOff(procedure) ??
_extensionIndex.getExtensionMemberForTearOff(procedure);
extensionIndex.getExtensionTypeMemberForTearOff(procedure) ??
extensionIndex.getExtensionMemberForTearOff(procedure);
if (tornOff != null) return tornOff.asMember;
final name = extractConstructorNameFromTearOff(procedure.name);
if (name == null) return null;
@ -895,10 +897,10 @@ class JsInteropChecks extends RecursiveVisitor {
if (member.isExternal) {
if (_classHasJSAnnotation) return true;
if (member.isExtensionMember) {
return _extensionIndex.getExtensionAnnotatable(member) != null;
return extensionIndex.getExtensionAnnotatable(member) != null;
}
if (member.isExtensionTypeMember) {
return _extensionIndex.getExtensionType(member) != null;
return extensionIndex.getExtensionType(member) != null;
}
if (member.enclosingClass == null) {
// dart:js_interop requires top-levels to be @JS-annotated. package:js
@ -926,11 +928,11 @@ class JsInteropChecks extends RecursiveVisitor {
// TODO(srujzs): We may want to support type parameters with primitive
// bounds that are themselves allowed e.g. `num`. If so, we should handle
// that change in dart2wasm.
if (_extensionIndex.isAllowedRepresentationType(bound)) return true;
if (extensionIndex.isAllowedRepresentationType(bound)) return true;
}
// If it can be used as a representation type of an interop extension type,
// it is okay to be used on an external member.
if (_extensionIndex.isAllowedRepresentationType(type)) return true;
if (extensionIndex.isAllowedRepresentationType(type)) return true;
if (type is InterfaceType) {
final cls = type.classNode;
// Primitive types are okay.

View file

@ -1,384 +0,0 @@
// 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.
// ignore_for_file: implementation_imports
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show templateJsInteropExportClassNotMarkedExportable;
import 'package:_js_interop_checks/js_interop_checks.dart'
show JsInteropDiagnosticReporter;
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
import 'package:front_end/src/fasta/fasta_codes.dart'
show
templateJsInteropExportInvalidInteropTypeArgument,
templateJsInteropExportInvalidTypeArgument;
import 'package:kernel/ast.dart';
import 'package:kernel/type_environment.dart';
import 'export_checker.dart';
import 'static_interop_mock_validator.dart';
class ExportCreator extends Transformer {
final Procedure _callMethodVarArgs;
final Procedure _createDartExport;
final Procedure _createJSInteropWrapper;
final Procedure _createStaticInteropMock;
final JsInteropDiagnosticReporter _diagnosticReporter;
final ExportChecker _exportChecker;
final Procedure _functionToJS;
final Procedure _getProperty;
final Procedure _globalContext;
final ExtensionTypeDeclaration _jsAny;
final ExtensionTypeDeclaration _jsObject;
final Procedure _setProperty;
final Procedure _stringToJS;
final StaticInteropMockValidator _staticInteropMockValidator;
final TypeEnvironment _typeEnvironment;
ExportCreator(
this._typeEnvironment, this._diagnosticReporter, this._exportChecker)
: _callMethodVarArgs = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop_unsafe',
'JSObjectUnsafeUtilExtension|callMethodVarArgs'),
_createDartExport = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createDartExport'),
_createJSInteropWrapper = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop', 'createJSInteropWrapper'),
_createStaticInteropMock = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createStaticInteropMock'),
_functionToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
_getProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop_unsafe', 'JSObjectUnsafeUtilExtension|[]'),
_globalContext = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop', 'get:globalContext'),
_jsAny = _typeEnvironment.coreTypes.index
.getExtensionType('dart:js_interop', 'JSAny'),
_jsObject = _typeEnvironment.coreTypes.index
.getExtensionType('dart:js_interop', 'JSObject'),
_setProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop_unsafe', 'JSObjectUnsafeUtilExtension|[]='),
_stringToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'StringToJSString|get#toJS'),
_staticInteropMockValidator = StaticInteropMockValidator(
_diagnosticReporter, _exportChecker, _typeEnvironment);
@override
TreeNode visitStaticInvocation(StaticInvocation node) {
final target = node.target;
if (target == _createDartExport || target == _createJSInteropWrapper) {
final typeArguments = node.arguments.types;
assert(typeArguments.length == 1);
if (_verifyExportable(node, typeArguments[0])) {
final interface = typeArguments[0] as InterfaceType;
if (target == _createJSInteropWrapper) {
return _createExport(node, interface,
ExtensionType(_jsObject, Nullability.nonNullable));
}
return _createExport(node, interface);
}
} else if (target == _createStaticInteropMock) {
final typeArguments = node.arguments.types;
assert(typeArguments.length == 2);
final staticInteropType = typeArguments[0];
final dartType = typeArguments[1];
final exportable = _verifyExportable(node, dartType);
final staticInteropTypeArgumentCorrect = _staticInteropMockValidator
.validateStaticInteropTypeArgument(node, staticInteropType);
final dartTypeArgumentCorrect =
_staticInteropMockValidator.validateDartTypeArgument(node, dartType);
if (exportable &&
staticInteropTypeArgumentCorrect &&
dartTypeArgumentCorrect &&
_staticInteropMockValidator.validateCreateStaticInteropMock(
node,
(staticInteropType as InterfaceType).classNode,
(dartType as InterfaceType).classNode)) {
final arguments = node.arguments.positional;
assert(arguments.length == 1 || arguments.length == 2);
final proto = arguments.length == 2 ? arguments[1] : null;
return _createExport(node, dartType, staticInteropType, proto);
}
}
node.transformChildren(this);
return node;
}
/// Validate that the [dartType] provided via `createDartExport` or
/// `createJSInteropWrapper` can be exported safely.
///
/// Checks that:
/// - Type argument is a valid Dart interface type.
/// - Type argument is not a JS interop type.
/// - Type argument was not marked as non-exportable.
///
/// If there were no errors with processing the class, returns true.
/// Otherwise, returns false.
bool _verifyExportable(StaticInvocation node, DartType dartType) {
if (dartType is! InterfaceType) {
_diagnosticReporter.report(
templateJsInteropExportInvalidTypeArgument.withArguments(
dartType, true),
node.fileOffset,
node.name.text.length,
node.location?.file);
return false;
}
var dartClass = dartType.classNode;
if (js_interop.hasJSInteropAnnotation(dartClass) ||
js_interop.hasStaticInteropAnnotation(dartClass) ||
js_interop.hasAnonymousAnnotation(dartClass)) {
_diagnosticReporter.report(
templateJsInteropExportInvalidInteropTypeArgument.withArguments(
dartType, true),
node.fileOffset,
node.name.text.length,
node.location?.file);
return false;
}
if (!_exportChecker.exportStatus.containsKey(dartClass.reference)) {
// This occurs when we deserialize previously compiled modules. Those
// modules may contain export classes, so we need to revisit the classes
// in those previously compiled modules if they are used.
for (var member in dartClass.procedures) {
_exportChecker.visitMember(member);
}
for (var member in dartClass.fields) {
_exportChecker.visitMember(member);
}
_exportChecker.visitClass(dartClass);
}
var exportStatus = _exportChecker.exportStatus[dartClass.reference];
if (exportStatus == ExportStatus.nonExportable) {
_diagnosticReporter.report(
templateJsInteropExportClassNotMarkedExportable
.withArguments(dartClass.name),
node.fileOffset,
node.name.text.length,
node.location?.file);
return false;
}
return exportStatus == ExportStatus.exportable;
}
/// Create the object literal using the export map that was computed from the
/// interface in [dartType].
///
/// [node] is either a call to `createStaticInteropMock`, `createDartExport`,
/// or `createJSInteropWrapper`. [dartType] is assumed to be a valid
/// exportable class. [returnType] is the type that the object literal will be
/// casted to. [proto] is an optional prototype object that users can pass to
/// instantiate the object literal.
///
/// The export map is already validated, so this method simply iterates over
/// it and either assigns a method for a given property name, or assigns a
/// getter and/or setter.
///
/// Returns a call to the block of code that instantiates this object literal
/// and returns it.
TreeNode _createExport(StaticInvocation node, InterfaceType dartType,
[DartType? returnType, Expression? proto]) {
Expression asJSObject(Expression object, [bool nullable = false]) =>
AsExpression(
object,
ExtensionType(_jsObject,
nullable ? Nullability.nullable : Nullability.nonNullable))
..fileOffset = node.fileOffset;
Expression toJSString(String string) =>
StaticInvocation(_stringToJS, Arguments([StringLiteral(string)]))
..fileOffset = node.fileOffset;
StaticInvocation callMethodVarArgs(Expression jsObject, String methodName,
List<Expression> args, DartType returnType) {
// `jsObject.callMethodVarArgs(methodName.toJS, args)`
return StaticInvocation(
_callMethodVarArgs,
Arguments([
jsObject,
toJSString(methodName),
ListLiteral(args,
typeArgument: ExtensionType(_jsAny, Nullability.nullable))
], types: [
returnType
]))
..fileOffset = node.fileOffset;
}
// Get the global 'Object' property.
Expression getObjectProperty() => asJSObject(StaticInvocation(_getProperty,
Arguments([StaticGet(_globalContext), StringLiteral('Object')])))
..fileOffset = node.fileOffset;
// Get a fresh object literal, using the proto to create it if one was
// given.
StaticInvocation getLiteral([Expression? proto]) {
return callMethodVarArgs(
getObjectProperty(),
'create',
[asJSObject(proto ?? NullLiteral(), true)],
ExtensionType(_jsObject, Nullability.nonNullable));
}
var exportMap =
_exportChecker.exportClassToMemberMap[dartType.classNode.reference]!;
var block = <Statement>[];
returnType ??= _typeEnvironment.coreTypes.objectNonNullableRawType;
var dartInstance = VariableDeclaration('#dartInstance',
initializer: node.arguments.positional[0],
type: dartType,
isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(dartInstance);
var jsExporter = VariableDeclaration('#jsExporter',
initializer: getLiteral(proto),
type: ExtensionType(_jsObject, Nullability.nonNullable),
isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(jsExporter);
for (var exportName in exportMap.keys) {
var exports = exportMap[exportName]!;
ExpressionStatement setProperty(
VariableGet jsObject, String propertyName, StaticInvocation jsValue) {
// `jsObject[propertyName] = jsValue`
return ExpressionStatement(StaticInvocation(_setProperty,
Arguments([jsObject, StringLiteral(propertyName), jsValue])))
..fileOffset = node.fileOffset
..parent = node.parent;
}
var firstExport = exports.first;
// With methods, there's only one export per export name.
if (firstExport is Procedure &&
firstExport.kind == ProcedureKind.Method) {
// `jsExport[jsName] = dartMock.tearoffMethod.toJS`
block.add(setProperty(
VariableGet(jsExporter),
exportName,
StaticInvocation(
_functionToJS,
Arguments([
InstanceTearOff(InstanceAccessKind.Instance,
VariableGet(dartInstance), firstExport.name,
interfaceTarget: firstExport,
resultType: _staticInteropMockValidator
.typeParameterResolver
.resolve(firstExport.getterType))
]))));
} else {
// Create the mapping from `get` and `set` to their `dartInstance` calls
// to be used in `Object.defineProperty`.
// Add the given exports to the mapping that corresponds to the given
// exportName that is used by `Object.defineProperty`. In order to
// conform to that API, this function defines 'get' or 'set' properties
// on a given object literal.
// The AST code looks like:
//
// ```
// getSetMap['get'] = () {
// return dartInstance.getter;
// }.toJS;
// ```
//
// in the case of a getter and:
//
// ```
// getSetMap['set'] = (val) {
// dartInstance.setter = val;
// }.toJS;
// ```
//
// in the case of a setter.
//
// A new map VariableDeclaration is created and added to the block of
// statements for each export name.
var getSetMap = VariableDeclaration('#${exportName}Mapping',
initializer: getLiteral(),
type: ExtensionType(_jsObject, Nullability.nonNullable),
isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(getSetMap);
var getSet = _exportChecker.getGetterSetter(exports);
var getter = getSet.getter;
var setter = getSet.setter;
if (getter != null) {
final resultType = _staticInteropMockValidator.typeParameterResolver
.resolve(getter.getterType);
block.add(setProperty(
VariableGet(getSetMap),
'get',
StaticInvocation(
_functionToJS,
Arguments([
FunctionExpression(FunctionNode(
ReturnStatement(InstanceGet(InstanceAccessKind.Instance,
VariableGet(dartInstance), getter.name,
interfaceTarget: getter, resultType: resultType)),
returnType: resultType))
]))));
}
if (setter != null) {
var setterParameter = VariableDeclaration('#val',
type: _staticInteropMockValidator.typeParameterResolver
.resolve(setter.setterType),
isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(setProperty(
VariableGet(getSetMap),
'set',
StaticInvocation(
_functionToJS,
Arguments([
FunctionExpression(FunctionNode(
ExpressionStatement(InstanceSet(
InstanceAccessKind.Instance,
VariableGet(dartInstance),
setter.name,
VariableGet(setterParameter),
interfaceTarget: setter)),
positionalParameters: [setterParameter],
returnType: const VoidType()))
]))));
}
// Call `Object.defineProperty` to define the export name with the
// 'get' and/or 'set' mapping. This allows us to treat get/set
// semantics as methods.
block.add(ExpressionStatement(callMethodVarArgs(
getObjectProperty(),
'defineProperty',
[
VariableGet(jsExporter),
toJSString(exportName),
VariableGet(getSetMap)
],
VoidType()))
..fileOffset = node.fileOffset
..parent = node.parent);
}
}
block.add(ReturnStatement(AsExpression(VariableGet(jsExporter), returnType)
..fileOffset = node.fileOffset));
// Return a call to evaluate the entire block of code and return the JS mock
// that was created.
return FunctionInvocation(
FunctionAccessKind.Function,
FunctionExpression(FunctionNode(Block(block), returnType: returnType)),
Arguments([]),
functionType: FunctionType([], returnType, Nullability.nonNullable))
..fileOffset = node.fileOffset
..parent = node.parent;
}
}

View file

@ -68,7 +68,8 @@ class JsUtilOptimizer extends Transformer {
late final ExtensionIndex _extensionIndex;
JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy)
JsUtilOptimizer(
this._coreTypes, ClassHierarchy hierarchy, this._extensionIndex)
: _callMethodTarget =
_coreTypes.index.getTopLevelProcedure('dart:js_util', 'callMethod'),
_callMethodTrustTypeTarget = _coreTypes.index
@ -108,10 +109,7 @@ class JsUtilOptimizer extends Transformer {
_listEmptyFactory =
_coreTypes.index.getProcedure('dart:core', 'List', 'empty'),
_staticTypeContext = StatefulStaticTypeContext.stacked(
TypeEnvironment(_coreTypes, hierarchy)) {
_extensionIndex =
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment);
}
TypeEnvironment(_coreTypes, hierarchy));
@override
visitLibrary(Library node) {
@ -200,7 +198,7 @@ class JsUtilOptimizer extends Transformer {
if (jsName.isNotEmpty) {
var lastDotIndex = jsName.lastIndexOf('.');
if (lastDotIndex != -1) {
dottedPrefix = _concatenateJSNames(
dottedPrefix = concatenateJSNames(
dottedPrefix, jsName.substring(0, lastDotIndex));
}
}
@ -231,15 +229,17 @@ class JsUtilOptimizer extends Transformer {
? enclosingClass.name
: (enclosingClass as ExtensionTypeDeclaration).name;
}
dottedPrefix = _concatenateJSNames(dottedPrefix, className);
dottedPrefix = concatenateJSNames(dottedPrefix, className);
}
return dottedPrefix;
}
// TODO(srujzs): It feels weird to have this be public, but it's weirder to
// have this method elsewhere for now. Figure out a better place for this.
/// Given two `@JS` values, combines them into a concatenated name using '.'.
///
/// If either parameters are empty, returns the other.
String _concatenateJSNames(String prefix, String suffix) {
static String concatenateJSNames(String prefix, String suffix) {
if (prefix.isEmpty) return suffix;
if (suffix.isEmpty) return prefix;
return '$prefix.$suffix';
@ -473,6 +473,8 @@ class JsUtilOptimizer extends Transformer {
invocation = _lowerCallMethod(node, shouldTrustType: false);
} else if (target == _callConstructorTarget) {
invocation = _lowerCallConstructor(node);
// TODO(srujzs): Delete the `isPatchedMember` check once
// https://github.com/dart-lang/sdk/issues/53367 is resolved.
} else if (target.isExternal && !JsInteropChecks.isPatchedMember(target)) {
final builder = _externalInvocationBuilders.putIfAbsent(
target, () => _getExternalInvocationBuilder(target));
@ -827,8 +829,7 @@ class ExtensionIndex {
if (_coreInteropTypeIndex.containsKey(reference)) {
return _coreInteropTypeIndex[reference];
}
if (declaration.enclosingLibrary.importUri.toString() ==
'dart:js_interop') {
if (isJSType(declaration)) {
return _coreInteropTypeIndex[reference] = reference;
}
// Note that we recurse instead of using the erasure, as JS types are
@ -991,4 +992,7 @@ class ExtensionIndex {
}
return false;
}
bool isJSType(ExtensionTypeDeclaration decl) =>
decl.enclosingLibrary.importUri.toString() == 'dart:js_interop';
}

View file

@ -0,0 +1,587 @@
// Copyright (c) 2024, 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.
// ignore_for_file: implementation_imports
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show
messageJsInteropIsATearoff,
templateJsInteropExportClassNotMarkedExportable;
import 'package:_js_interop_checks/js_interop_checks.dart'
show JsInteropDiagnosticReporter;
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
import 'package:front_end/src/fasta/fasta_codes.dart'
show
templateJsInteropExportInvalidInteropTypeArgument,
templateJsInteropExportInvalidTypeArgument,
templateJsInteropIsAInvalidType,
templateJsInteropIsAObjectLiteralType,
templateJsInteropIsAPrimitiveExtensionType;
import 'package:kernel/ast.dart';
import 'package:kernel/library_index.dart';
import 'package:kernel/type_environment.dart';
import 'export_checker.dart';
import 'js_util_optimizer.dart';
import 'static_interop_mock_validator.dart';
class SharedInteropTransformer extends Transformer {
final Procedure _callMethodVarArgs;
final Procedure _createDartExport;
final Procedure _createJSInteropWrapper;
final Procedure _createStaticInteropMock;
final JsInteropDiagnosticReporter _diagnosticReporter;
final ExportChecker _exportChecker;
final ExtensionIndex _extensionIndex;
final Procedure _functionToJS;
final Procedure _getProperty;
final Procedure _globalContext;
late bool _inIsATearoff;
final Procedure _instanceof;
final Procedure _instanceOfString;
late StaticInvocation? _invocation;
final Procedure _isA;
final Procedure _isATearoff;
final ExtensionTypeDeclaration _jsAny;
final ExtensionTypeDeclaration _jsFunction;
final ExtensionTypeDeclaration _jsObject;
final Procedure _setProperty;
final Procedure _stringToJS;
final StaticInteropMockValidator _staticInteropMockValidator;
final TypeEnvironment _typeEnvironment;
final Procedure _typeofEquals;
StaticInvocation get invocation => _invocation!;
SharedInteropTransformer(this._typeEnvironment, this._diagnosticReporter,
this._exportChecker, this._extensionIndex)
: _callMethodVarArgs = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop_unsafe',
'JSObjectUnsafeUtilExtension|callMethodVarArgs'),
_createDartExport = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createDartExport'),
_createJSInteropWrapper = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop', 'createJSInteropWrapper'),
_createStaticInteropMock = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createStaticInteropMock'),
_functionToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
_getProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop_unsafe', 'JSObjectUnsafeUtilExtension|[]'),
_globalContext = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_interop', 'get:globalContext'),
_instanceof = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'JSAnyUtilityExtension|instanceof'),
_instanceOfString = _typeEnvironment.coreTypes.index
.getTopLevelProcedure(
'dart:js_interop', 'JSAnyUtilityExtension|instanceOfString'),
_isA = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'JSAnyUtilityExtension|isA'),
_isATearoff = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop',
'JSAnyUtilityExtension|${LibraryIndex.tearoffPrefix}isA'),
_jsAny = _typeEnvironment.coreTypes.index
.getExtensionType('dart:js_interop', 'JSAny'),
_jsFunction = _typeEnvironment.coreTypes.index
.getExtensionType('dart:js_interop', 'JSFunction'),
_jsObject = _typeEnvironment.coreTypes.index
.getExtensionType('dart:js_interop', 'JSObject'),
_setProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop_unsafe', 'JSObjectUnsafeUtilExtension|[]='),
_stringToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'StringToJSString|get#toJS'),
_staticInteropMockValidator = StaticInteropMockValidator(
_diagnosticReporter, _exportChecker, _typeEnvironment),
_typeofEquals = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'JSAnyUtilityExtension|typeofEquals');
@override
TreeNode visitStaticInvocation(StaticInvocation node) {
_invocation = node;
TreeNode replacement = invocation;
final target = invocation.target;
if (target == _createDartExport || target == _createJSInteropWrapper) {
final typeArguments = invocation.arguments.types;
assert(typeArguments.length == 1);
if (_verifyExportable(typeArguments[0])) {
final interface = typeArguments[0] as InterfaceType;
if (target == _createJSInteropWrapper) {
replacement = _createExport(
interface, ExtensionType(_jsObject, Nullability.nonNullable));
}
replacement = _createExport(interface);
}
} else if (target == _createStaticInteropMock) {
final typeArguments = invocation.arguments.types;
assert(typeArguments.length == 2);
final staticInteropType = typeArguments[0];
final dartType = typeArguments[1];
final exportable = _verifyExportable(dartType);
final staticInteropTypeArgumentCorrect = _staticInteropMockValidator
.validateStaticInteropTypeArgument(invocation, staticInteropType);
final dartTypeArgumentCorrect = _staticInteropMockValidator
.validateDartTypeArgument(invocation, dartType);
if (exportable &&
staticInteropTypeArgumentCorrect &&
dartTypeArgumentCorrect &&
_staticInteropMockValidator.validateCreateStaticInteropMock(
invocation,
(staticInteropType as InterfaceType).classNode,
(dartType as InterfaceType).classNode)) {
final arguments = invocation.arguments.positional;
assert(arguments.length == 1 || arguments.length == 2);
final proto = arguments.length == 2 ? arguments[1] : null;
replacement = _createExport(dartType, staticInteropType, proto);
}
} else if (target == _isA) {
final receiver = invocation.arguments.positional[0];
final typeArguments = invocation.arguments.types;
assert(typeArguments.length == 1);
final interopType = typeArguments[0];
final coreInteropType =
_extensionIndex.getCoreInteropType(interopType)?.node;
if (coreInteropType is ExtensionTypeDeclaration &&
_extensionIndex.isJSType(coreInteropType)) {
replacement = _createIsACheck(
receiver, interopType as ExtensionType, coreInteropType);
} else {
// Generated tear-offs call the original method, so ignore that
// invocation.
if (!_inIsATearoff) {
_diagnosticReporter.report(
templateJsInteropIsAInvalidType.withArguments(interopType, true),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
}
replacement = invocation;
}
} else if (target == _isATearoff) {
// Calling the generated tear-off is still bad, however.
_diagnosticReporter.report(
messageJsInteropIsATearoff,
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
}
replacement.transformChildren(this);
_invocation = null;
return replacement;
}
@override
TreeNode visitProcedure(Procedure node) {
_inIsATearoff = node == _isATearoff;
node.transformChildren(this);
_inIsATearoff = false;
return node;
}
/// Validate that the [dartType] provided via `createDartExport` or
/// `createJSInteropWrapper` can be exported safely.
///
/// Checks that:
/// - Type argument is a valid Dart interface type.
/// - Type argument is not a JS interop type.
/// - Type argument was not marked as non-exportable.
///
/// If there were no errors with processing the class, returns true.
/// Otherwise, returns false.
bool _verifyExportable(DartType dartType) {
if (dartType is! InterfaceType) {
_diagnosticReporter.report(
templateJsInteropExportInvalidTypeArgument.withArguments(
dartType, true),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
return false;
}
var dartClass = dartType.classNode;
if (js_interop.hasJSInteropAnnotation(dartClass) ||
js_interop.hasStaticInteropAnnotation(dartClass) ||
js_interop.hasAnonymousAnnotation(dartClass)) {
_diagnosticReporter.report(
templateJsInteropExportInvalidInteropTypeArgument.withArguments(
dartType, true),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
return false;
}
if (!_exportChecker.exportStatus.containsKey(dartClass.reference)) {
// This occurs when we deserialize previously compiled modules. Those
// modules may contain export classes, so we need to revisit the classes
// in those previously compiled modules if they are used.
for (var member in dartClass.procedures) {
_exportChecker.visitMember(member);
}
for (var member in dartClass.fields) {
_exportChecker.visitMember(member);
}
_exportChecker.visitClass(dartClass);
}
var exportStatus = _exportChecker.exportStatus[dartClass.reference];
if (exportStatus == ExportStatus.nonExportable) {
_diagnosticReporter.report(
templateJsInteropExportClassNotMarkedExportable
.withArguments(dartClass.name),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
return false;
}
return exportStatus == ExportStatus.exportable;
}
/// Create the object literal using the export map that was computed from the
/// interface in [dartType].
///
/// [dartType] is assumed to be a valid exportable class. [returnType] is the
/// type that the object literal will be casted to. [proto] is an optional
/// prototype object that users can pass to instantiate the object literal.
///
/// The export map is already validated, so this method simply iterates over
/// it and either assigns a method for a given property name, or assigns a
/// getter and/or setter.
///
/// Returns a call to the block of code that instantiates this object literal
/// and returns it.
TreeNode _createExport(InterfaceType dartType,
[DartType? returnType, Expression? proto]) {
var exportMap =
_exportChecker.exportClassToMemberMap[dartType.classNode.reference]!;
var block = <Statement>[];
returnType ??= _typeEnvironment.coreTypes.objectNonNullableRawType;
// TODO(srujzs): We can avoid creating a variable if the passed-in value is
// already a variable for both these declarations.
// TODO(srujzs): Change these to `VariableDeclaration.forValue` once
// https://github.com/dart-lang/sdk/issues/54734 is resolved.
var dartInstance = VariableDeclaration('#dartInstance',
initializer: invocation.arguments.positional[0],
type: dartType,
isSynthesized: true)
..fileOffset = invocation.fileOffset;
block.add(dartInstance);
var jsExporter = VariableDeclaration('#jsExporter',
initializer: getLiteral(proto),
type: ExtensionType(_jsObject, Nullability.nonNullable),
isSynthesized: true)
..fileOffset = invocation.fileOffset;
block.add(jsExporter);
for (var exportName in exportMap.keys) {
var exports = exportMap[exportName]!;
ExpressionStatement setProperty(
VariableGet jsObject, String propertyName, StaticInvocation jsValue) {
// `jsObject[propertyName] = jsValue`
return ExpressionStatement(StaticInvocation(_setProperty,
Arguments([jsObject, StringLiteral(propertyName), jsValue])))
..fileOffset = invocation.fileOffset;
}
var firstExport = exports.first;
// With methods, there's only one export per export name.
if (firstExport is Procedure &&
firstExport.kind == ProcedureKind.Method) {
// `jsExport[jsName] = dartMock.tearoffMethod.toJS`
block.add(setProperty(
VariableGet(jsExporter),
exportName,
StaticInvocation(
_functionToJS,
Arguments([
InstanceTearOff(InstanceAccessKind.Instance,
VariableGet(dartInstance), firstExport.name,
interfaceTarget: firstExport,
resultType: _staticInteropMockValidator
.typeParameterResolver
.resolve(firstExport.getterType))
]))));
} else {
// Create the mapping from `get` and `set` to their `dartInstance` calls
// to be used in `Object.defineProperty`.
// Add the given exports to the mapping that corresponds to the given
// exportName that is used by `Object.defineProperty`. In order to
// conform to that API, this function defines 'get' or 'set' properties
// on a given object literal.
// The AST code looks like:
//
// ```
// getSetMap['get'] = () {
// return dartInstance.getter;
// }.toJS;
// ```
//
// in the case of a getter and:
//
// ```
// getSetMap['set'] = (val) {
// dartInstance.setter = val;
// }.toJS;
// ```
//
// in the case of a setter.
//
// A new map VariableDeclaration is created and added to the block of
// statements for each export name.
var getSetMap = VariableDeclaration('#${exportName}Mapping',
initializer: getLiteral(),
type: ExtensionType(_jsObject, Nullability.nonNullable),
isSynthesized: true)
..fileOffset = invocation.fileOffset;
block.add(getSetMap);
var getSet = _exportChecker.getGetterSetter(exports);
var getter = getSet.getter;
var setter = getSet.setter;
if (getter != null) {
final resultType = _staticInteropMockValidator.typeParameterResolver
.resolve(getter.getterType);
block.add(setProperty(
VariableGet(getSetMap),
'get',
StaticInvocation(
_functionToJS,
Arguments([
FunctionExpression(FunctionNode(
ReturnStatement(InstanceGet(InstanceAccessKind.Instance,
VariableGet(dartInstance), getter.name,
interfaceTarget: getter, resultType: resultType)),
returnType: resultType))
]))));
}
if (setter != null) {
var setterParameter = VariableDeclaration('#val',
type: _staticInteropMockValidator.typeParameterResolver
.resolve(setter.setterType),
isSynthesized: true)
..fileOffset = invocation.fileOffset;
block.add(setProperty(
VariableGet(getSetMap),
'set',
StaticInvocation(
_functionToJS,
Arguments([
FunctionExpression(FunctionNode(
ExpressionStatement(InstanceSet(
InstanceAccessKind.Instance,
VariableGet(dartInstance),
setter.name,
VariableGet(setterParameter),
interfaceTarget: setter)),
positionalParameters: [setterParameter],
returnType: const VoidType()))
]))));
}
// Call `Object.defineProperty` to define the export name with the
// 'get' and/or 'set' mapping. This allows us to treat get/set
// semantics as methods.
block.add(ExpressionStatement(callMethodVarArgs(
getObjectProperty(),
'defineProperty',
[
VariableGet(jsExporter),
toJSString(exportName),
VariableGet(getSetMap)
],
VoidType()))
..fileOffset = invocation.fileOffset);
}
}
block.add(ReturnStatement(AsExpression(VariableGet(jsExporter), returnType)
..fileOffset = invocation.fileOffset));
// Return a call to evaluate the entire block of code and return the JS mock
// that was created.
return FunctionInvocation(
FunctionAccessKind.Function,
FunctionExpression(FunctionNode(Block(block), returnType: returnType)),
Arguments([]),
functionType: FunctionType([], returnType, Nullability.nonNullable))
..fileOffset = invocation.fileOffset
..parent = invocation.parent;
}
/// Create and return an appropriate type-check for the given [receiver] and
/// [interopType].
///
/// [jsType] is the JS type that [interopType] is or wraps.
///
/// If [interopType] is an erroneous type like a type that wraps a JS
/// primitive type or one with an object literal constructor, an error is
/// reported.
TreeNode _createIsACheck(Expression receiver, ExtensionType interopType,
ExtensionTypeDeclaration jsType) {
// In the case where the receiver wasn't a variable to begin with,
// synthesize a var so that we don't evaluate the receiver multiple times.
final any = receiver is VariableGet
? receiver.variable
: (VariableDeclaration.forValue(receiver,
type: ExtensionType(_jsAny, Nullability.nullable))
..fileOffset = invocation.fileOffset);
Expression? check;
final interopTypeDecl = interopType.extensionTypeDeclaration;
final jsTypeName = jsType.name;
String? typeofString;
String? instanceOfString;
switch (jsTypeName) {
case 'JSAny' when interopTypeDecl == jsType:
// Only avoids any non null-related checks when users are referring
// directly to the `dart:js_interop` type and not some wrapper.
break;
case 'JSNumber':
typeofString = 'number';
break;
case 'JSBoolean':
typeofString = 'boolean';
break;
case 'JSString':
typeofString = 'string';
break;
case 'JSBigInt':
typeofString = 'bigint';
break;
case 'JSSymbol':
typeofString = 'symbol';
break;
case 'JSTypedArray' when interopTypeDecl == jsType:
// Only do this special case when users are referring directly to
// the `dart:js_interop` type and not some wrapper.
// `TypedArray` doesn't exist as a property in JS, but rather as a
// superclass of all typed arrays. In order to do the most sensible
// thing here, we can use the prototype of some typed array, and
// check that the receiver is an `instanceof` that prototype.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#description
// for more details.
check = StaticInvocation(_instanceof,
Arguments([VariableGet(any), getInt8ArrayPrototype()]));
break;
default:
for (final descriptor in interopTypeDecl.memberDescriptors) {
final descriptorNode = descriptor.memberReference.node;
if (descriptorNode is Procedure &&
_extensionIndex.isLiteralConstructor(descriptorNode)) {
_diagnosticReporter.report(
templateJsInteropIsAObjectLiteralType.withArguments(
interopType, true),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
break;
}
}
// Currently, any type that is not recursively a JS primitive type or
// `JSTypedArray` will use the written type (with renames) to do a
// type check. This means using any extension type that wraps
// `JSArray` in `isA` will use the extension type's name instead of
// `Array`. This may lead to confusion when a user writes an
// extension type that wraps `JSArray` but did not intend to give it
// a separate JS type. We could change this, but that means users
// can't use any actual subtypes of `JSArray`. Users who want to do
// a check against `Array` can always use `isA<JSArray>()` or use
// `@JS('Array')` (with no library scoping) to provide the right
// type.
var typeName = js_interop.getJSName(interopTypeDecl);
if (typeName.isEmpty) typeName = interopTypeDecl.name;
instanceOfString = JsUtilOptimizer.concatenateJSNames(
js_interop.getJSName(interopTypeDecl.enclosingLibrary), typeName);
}
if (typeofString != null) {
if (interopTypeDecl != jsType) {
_diagnosticReporter.report(
templateJsInteropIsAPrimitiveExtensionType.withArguments(
interopType, jsTypeName, true),
invocation.fileOffset,
invocation.name.text.length,
invocation.location?.file);
} else {
check = StaticInvocation(_typeofEquals,
Arguments([VariableGet(any), StringLiteral(typeofString)]));
}
} else if (instanceOfString != null) {
check = StaticInvocation(_instanceOfString,
Arguments([VariableGet(any), StringLiteral(instanceOfString)]));
}
final nullable = interopType.nullability == Nullability.nullable;
Expression nullCheck = EqualsNull(VariableGet(any));
Expression notNullCheck = Not(EqualsNull(VariableGet(any)));
if (check != null) {
check = nullable
? LogicalExpression(nullCheck, LogicalExpressionOperator.OR, check)
: LogicalExpression(
notNullCheck, LogicalExpressionOperator.AND, check);
} else if (!nullable) {
// `JSAny`
check = notNullCheck;
} else {
// `JSAny?`
check = BoolLiteral(true);
}
return receiver is VariableGet ? check : Let(any, check)
..fileOffset = invocation.fileOffset
..parent = invocation.parent;
}
// Various shared helpers to make calls to `dart:js_interop`/
// `dart:js_interop_unsafe` members easier. These helpers need to access the
// current node's file offset so that the CFE verifier is happy.
Expression asJSObject(Expression object,
[bool nullable = false]) =>
AsExpression(
object,
ExtensionType(_jsObject,
nullable ? Nullability.nullable : Nullability.nonNullable))
..fileOffset = invocation.fileOffset;
Expression toJSString(String string) =>
StaticInvocation(_stringToJS, Arguments([StringLiteral(string)]))
..fileOffset = invocation.fileOffset;
StaticInvocation callMethodVarArgs(Expression jsObject, String methodName,
List<Expression> args, DartType returnType) {
// `jsObject.callMethodVarArgs(methodName.toJS, args)`
return StaticInvocation(
_callMethodVarArgs,
Arguments([
jsObject,
toJSString(methodName),
ListLiteral(args,
typeArgument: ExtensionType(_jsAny, Nullability.nullable))
], types: [
returnType
]))
..fileOffset = invocation.fileOffset;
}
// Get the object with the given [property] off of the global context.
Expression getGlobalProperty(String property) => asJSObject(StaticInvocation(
_getProperty,
Arguments([StaticGet(_globalContext), StringLiteral(property)])))
..fileOffset = invocation.fileOffset;
Expression getObjectProperty() => getGlobalProperty('Object');
Expression getInt8ArrayPrototype() => callMethodVarArgs(
getObjectProperty(),
'getPrototypeOf',
[getGlobalProperty('Int8Array')],
ExtensionType(_jsFunction, Nullability.nonNullable));
// Get a fresh object literal, using the proto to create it if one was
// given.
StaticInvocation getLiteral([Expression? proto]) => callMethodVarArgs(
getObjectProperty(),
'create',
[asJSObject(proto ?? NullLiteral(), true)],
ExtensionType(_jsObject, Nullability.nonNullable));
}

View file

@ -9,8 +9,8 @@ library compiler.src.kernel.dart2js_target;
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show Message, LocatedMessage;
import 'package:_js_interop_checks/js_interop_checks.dart';
import 'package:_js_interop_checks/src/transformations/export_creator.dart';
import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart';
import 'package:_js_interop_checks/src/transformations/shared_interop_transformer.dart';
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
@ -155,13 +155,17 @@ class Dart2jsTarget extends Target {
for (var library in libraries) {
jsInteropChecks.visitLibrary(library);
}
var exportCreator = ExportCreator(TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter, jsInteropChecks.exportChecker);
var jsUtilOptimizer = JsUtilOptimizer(coreTypes, hierarchy);
var sharedInteropTransformer = SharedInteropTransformer(
TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter,
jsInteropChecks.exportChecker,
jsInteropChecks.extensionIndex);
var jsUtilOptimizer =
JsUtilOptimizer(coreTypes, hierarchy, jsInteropChecks.extensionIndex);
for (var library in libraries) {
// Export creator has static checks, so we still visit even if there are
// errors.
exportCreator.visitLibrary(library);
// Shared transformer has static checks, so we still visit even if there
// are errors.
sharedInteropTransformer.visitLibrary(library);
// TODO (rileyporter): Merge js_util optimizations with other lowerings
// in the single pass in `transformations/lowering.dart`.
if (!jsInteropReporter.hasJsInteropErrors) {

View file

@ -6,7 +6,7 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show Message, LocatedMessage;
import 'package:_js_interop_checks/js_interop_checks.dart';
import 'package:_js_interop_checks/src/js_interop.dart' as jsInteropHelper;
import 'package:_js_interop_checks/src/transformations/export_creator.dart';
import 'package:_js_interop_checks/src/transformations/shared_interop_transformer.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/clone.dart';
@ -215,10 +215,13 @@ class WasmTarget extends Target {
for (Library library in interopDependentLibraries) {
jsInteropChecks.visitLibrary(library);
}
final exportCreator = ExportCreator(TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter, jsInteropChecks.exportChecker);
final sharedInteropTransformer = SharedInteropTransformer(
TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter,
jsInteropChecks.exportChecker,
jsInteropChecks.extensionIndex);
for (Library library in interopDependentLibraries) {
exportCreator.visitLibrary(library);
sharedInteropTransformer.visitLibrary(library);
}
}

View file

@ -7,8 +7,8 @@ import 'dart:collection';
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show Message, LocatedMessage;
import 'package:_js_interop_checks/js_interop_checks.dart';
import 'package:_js_interop_checks/src/transformations/export_creator.dart';
import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart';
import 'package:_js_interop_checks/src/transformations/shared_interop_transformer.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/kernel.dart' hide Pattern;
@ -206,13 +206,17 @@ class DevCompilerTarget extends Target {
// Process and validate first before doing anything with exports.
node.accept(jsInteropChecks);
}
final exportCreator = ExportCreator(TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter, jsInteropChecks.exportChecker);
final jsUtilOptimizer = JsUtilOptimizer(coreTypes, hierarchy);
final sharedInteropTransformer = SharedInteropTransformer(
TypeEnvironment(coreTypes, hierarchy),
jsInteropReporter,
jsInteropChecks.exportChecker,
jsInteropChecks.extensionIndex);
final jsUtilOptimizer =
JsUtilOptimizer(coreTypes, hierarchy, jsInteropChecks.extensionIndex);
for (var node in nodes) {
_CovarianceTransformer(node).transform();
// Export creator has static checks, so we still visit.
node.accept(exportCreator);
// Shared interop transformer has static checks, so we still visit.
node.accept(sharedInteropTransformer);
if (!jsInteropReporter.hasJsInteropErrors) {
// We can't guarantee calls are well-formed, so don't transform.
node.accept(jsUtilOptimizer);

View file

@ -4108,6 +4108,109 @@ Message _withArgumentsJsInteropFunctionToJSRequiresStaticType(
arguments: {'type': _type});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateJsInteropIsAInvalidType = const Template<
Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropIsAInvalidType",
problemMessageTemplate:
r"""Type argument '#type' needs to be an interop 'ExtensionType'.""",
correctionMessageTemplate:
r"""Use a valid interop extension type as the type argument instead.""",
withArguments: _withArgumentsJsInteropIsAInvalidType);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>
codeJsInteropIsAInvalidType =
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropIsAInvalidType",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropIsAInvalidType(
DartType _type, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> typeParts = labeler.labelType(_type);
String type = typeParts.join();
return new Message(codeJsInteropIsAInvalidType,
problemMessage:
"""Type argument '${type}' needs to be an interop 'ExtensionType'.""" +
labeler.originMessages,
correctionMessage: """Use a valid interop extension type as the type argument instead.""",
arguments: {'type': _type});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateJsInteropIsAObjectLiteralType = const Template<
Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropIsAObjectLiteralType",
problemMessageTemplate:
r"""Type argument '#type' has an object literal constructor. Because 'isA' uses the type's name or '@JS()' rename, this may result in an incorrect type check.""",
correctionMessageTemplate:
r"""Use 'JSObject' as the type argument instead.""",
withArguments: _withArgumentsJsInteropIsAObjectLiteralType);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>
codeJsInteropIsAObjectLiteralType =
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropIsAObjectLiteralType",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropIsAObjectLiteralType(
DartType _type, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> typeParts = labeler.labelType(_type);
String type = typeParts.join();
return new Message(codeJsInteropIsAObjectLiteralType,
problemMessage:
"""Type argument '${type}' has an object literal constructor. Because 'isA' uses the type's name or '@JS()' rename, this may result in an incorrect type check.""" +
labeler.originMessages,
correctionMessage: """Use 'JSObject' as the type argument instead.""",
arguments: {'type': _type});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(
DartType _type, String string, bool isNonNullableByDefault)>
templateJsInteropIsAPrimitiveExtensionType = const Template<
Message Function(
DartType _type, String string, bool isNonNullableByDefault)>(
"JsInteropIsAPrimitiveExtensionType",
problemMessageTemplate:
r"""Type argument '#type' wraps primitive JS type '#string', which is specially handled using 'typeof'.""",
correctionMessageTemplate:
r"""Use the primitive JS type '#string' as the type argument instead.""",
withArguments: _withArgumentsJsInteropIsAPrimitiveExtensionType);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<
Message Function(
DartType _type, String string, bool isNonNullableByDefault)>
codeJsInteropIsAPrimitiveExtensionType = const Code<
Message Function(
DartType _type, String string, bool isNonNullableByDefault)>(
"JsInteropIsAPrimitiveExtensionType",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsJsInteropIsAPrimitiveExtensionType(
DartType _type, String string, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> typeParts = labeler.labelType(_type);
if (string.isEmpty) throw 'No string provided';
String type = typeParts.join();
return new Message(codeJsInteropIsAPrimitiveExtensionType,
problemMessage:
"""Type argument '${type}' wraps primitive JS type '${string}', which is specially handled using 'typeof'.""" +
labeler.originMessages,
correctionMessage: """Use the primitive JS type '${string}' as the type argument instead.""",
arguments: {'type': _type, 'string': string});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateJsInteropStaticInteropExternalTypeViolation = const Template<

View file

@ -631,6 +631,14 @@ JsInteropExternalExtensionMemberWithStaticDisallowed/analyzerCode: Fail # Web co
JsInteropExternalExtensionMemberWithStaticDisallowed/example: Fail # Web compiler specific
JsInteropExternalMemberNotJSAnnotated/analyzerCode: Fail # Web compiler specific
JsInteropExternalMemberNotJSAnnotated/example: Fail # Web compiler specific
JsInteropIsAInvalidType/analyzerCode: Fail # Web compiler specific
JsInteropIsAInvalidType/example: Fail # Web compiler specific
JsInteropIsAObjectLiteralType/analyzerCode: Fail # Web compiler specific
JsInteropIsAObjectLiteralType/example: Fail # Web compiler specific
JsInteropIsAPrimitiveExtensionType/analyzerCode: Fail # Web compiler specific
JsInteropIsAPrimitiveExtensionType/example: Fail # Web compiler specific
JsInteropIsATearoff/analyzerCode: Fail # Web compiler specific
JsInteropIsATearoff/example: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/analyzerCode: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/example: Fail # Web compiler specific
JsInteropFunctionToJSTypeParameters/analyzerCode: Fail # Web compiler specific

View file

@ -5845,6 +5845,22 @@ JsInteropExportNoExportableMembers:
problemMessage: "Class '#name' has no exportable members in the class or the inheritance chain."
correctionMessage: "Using `@JSExport`, annotate at least one instance member with a body or annotate a class that has such a member in the inheritance chain."
JsInteropIsAInvalidType:
problemMessage: "Type argument '#type' needs to be an interop 'ExtensionType'."
correctionMessage: "Use a valid interop extension type as the type argument instead."
JsInteropIsAObjectLiteralType:
problemMessage: "Type argument '#type' has an object literal constructor. Because 'isA' uses the type's name or '@JS()' rename, this may result in an incorrect type check."
correctionMessage: "Use 'JSObject' as the type argument instead."
JsInteropIsAPrimitiveExtensionType:
problemMessage: "Type argument '#type' wraps primitive JS type '#string', which is specially handled using 'typeof'."
correctionMessage: "Use the primitive JS type '#string' as the type argument instead."
JsInteropIsATearoff:
problemMessage: "'isA' can't be torn off."
correctionMessage: "Use a method that calls 'isA' and tear off that method instead."
JsInteropExtensionTypeNotInterop:
problemMessage: "Extension type '#name' is marked with a '@JS' annotation, but its representation type is not a valid JS interop type: '#type'."
correctionMessage: "Try declaring a valid JS interop representation type, which may include 'dart:js_interop' types, '@staticInterop' types, 'dart:html' types, or other interop extension types."

View file

@ -51,6 +51,7 @@ e.g
enclose
exhaustively
exportable
extensiontype
f
ffi
finality
@ -66,6 +67,7 @@ interact
interop
intervening
irrefutable
isa
js_util
jsany
jsexport
@ -113,6 +115,7 @@ resource
sdksummary
size
solutions
specially
stacktrace
stand
staticinterop
@ -130,7 +133,9 @@ this.namedconstructor
this.x
tojs
trusttypes
type's
type3.#name
typeof
u
unavailable
unsound

View file

@ -590,6 +590,7 @@ prerequisite
press
pretends
preventing
primitives
printouts
processes
processors

View file

@ -0,0 +1,52 @@
// Copyright (c) 2024, 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.
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
void test(JSAny any) {
// Top type.
any.isA<JSAny>();
any.isA<JSAny?>();
// Primitives.
any.isA<JSString>();
any.isA<JSString?>();
any.isA<JSNumber>();
any.isA<JSBoolean>();
any.isA<JSSymbol>();
any.isA<JSBigInt>();
// Objects.
any.isA<JSObject>();
any.isA<JSObject?>();
any.isA<JSArray>();
// JSTypedArray is handled differently.
any.isA<JSTypedArray>();
// User-defined types.
any.isA<CustomJSAny>();
any.isA<CustomJSObject>();
any.isA<CustomTypedArray>();
any.isA<CustomJSArray>();
// Non-variable expression should avoid evaluating twice.
() {
return any;
}()
.isA<JSAny>();
}
void main() {}

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */ {
lowered final self::CustomJSObject /* = _interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = _interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = _interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = _interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,88 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop_unsafe" as js_2;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */ {
lowered final self::CustomJSObject /* = _interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
!(any == null);
true;
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "string");
any == null || js_::JSAnyUtilityExtension|typeofEquals(any, "string");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "number");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "boolean");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "symbol");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "bigint");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
any == null || js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Array");
!(any == null) && js_::JSAnyUtilityExtension|instanceof(any, js_2::JSObjectUnsafeUtilExtension|callMethodVarArgs<js_::JSFunction /* = _interceptors::JavaScriptFunction */>(js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Object") as{ForLegacy} js_::JSObject /* = _interceptors::JSObject */, js_::StringToJSString|get#toJS("getPrototypeOf"), <js_::JSAny? /* = dart.core::Object? */>[js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Int8Array") as{ForLegacy} js_::JSObject /* = _interceptors::JSObject */]));
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSAny");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSObject");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomTypedArray");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.RenamedJSArray");
let final js_::JSAny? /* = dart.core::Object? */ #t1 = (() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */} in !(#t1 == null);
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,18 @@
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
void test(JSAny any) {}
void main() {}

View file

@ -0,0 +1,17 @@
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
void main() {}
void test(JSAny any) {}

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */ {
lowered final self::CustomJSObject /* = _interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = _interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = _interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = _interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */ {
lowered final self::CustomJSObject /* = _interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = _interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = _interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = _interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = _interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,54 @@
@dart.js_interop::JS::•("library1")
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@js_::JS::•("RenamedJSArray")
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
;
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
;
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
;
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
;
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void
;
static method main() → void
;
Extra constant evaluation status:
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///isa.dart:5:2 -> InstanceConstant(const JS{JS.name: "library1"})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///isa.dart:16:2 -> InstanceConstant(const JS{JS.name: "RenamedJSArray"})
Extra constant evaluation: evaluated: 10, effectively constant: 2

View file

@ -0,0 +1,88 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop_unsafe" as js_2;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = _interceptors::JSObject */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = _interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = _interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */ {
lowered final self::CustomJSObject /* = _interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = _interceptors::JSObject */ _) → self::CustomJSObject /* = _interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = _interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = _interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
!(any == null);
true;
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "string");
any == null || js_::JSAnyUtilityExtension|typeofEquals(any, "string");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "number");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "boolean");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "symbol");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "bigint");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
any == null || js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Array");
!(any == null) && js_::JSAnyUtilityExtension|instanceof(any, js_2::JSObjectUnsafeUtilExtension|callMethodVarArgs<js_::JSFunction /* = _interceptors::JavaScriptFunction */>(js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Object") as{ForLegacy} js_::JSObject /* = _interceptors::JSObject */, js_::StringToJSString|get#toJS("getPrototypeOf"), <js_::JSAny? /* = dart.core::Object? */>[js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Int8Array") as{ForLegacy} js_::JSObject /* = _interceptors::JSObject */]));
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSAny");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSObject");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomTypedArray");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.RenamedJSArray");
let final js_::JSAny? /* = dart.core::Object? */ #t1 = (() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */} in !(#t1 == null);
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,52 @@
// Copyright (c) 2024, 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.
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
void test(JSAny any) {
// Top type.
any.isA<JSAny>();
any.isA<JSAny?>();
// Primitives.
any.isA<JSString>();
any.isA<JSString?>();
any.isA<JSNumber>();
any.isA<JSBoolean>();
any.isA<JSSymbol>();
any.isA<JSBigInt>();
// Objects.
any.isA<JSObject>();
any.isA<JSObject?>();
any.isA<JSArray>();
// JSTypedArray is handled differently.
any.isA<JSTypedArray>();
// User-defined types.
any.isA<CustomJSAny>();
any.isA<CustomJSObject>();
any.isA<CustomTypedArray>();
any.isA<CustomJSArray>();
// Non-variable expression should avoid evaluating twice.
() {
return any;
}()
.isA<JSAny>();
}
void main() {}

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */ {
lowered final self::CustomJSObject /* = dart._interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = dart._interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = dart._interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = dart._interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,88 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop_unsafe" as js_2;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */ {
lowered final self::CustomJSObject /* = dart._interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
!(any == null);
true;
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "string");
any == null || js_::JSAnyUtilityExtension|typeofEquals(any, "string");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "number");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "boolean");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "symbol");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "bigint");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
any == null || js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Array");
!(any == null) && js_::JSAnyUtilityExtension|instanceof(any, js_2::JSObjectUnsafeUtilExtension|callMethodVarArgs<js_::JSFunction /* = dart._interceptors::JavaScriptFunction */>(js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Object") as{ForLegacy} js_::JSObject /* = dart._interceptors::JSObject */, js_::StringToJSString|get#toJS("getPrototypeOf"), <js_::JSAny? /* = dart.core::Object? */>[js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Int8Array") as{ForLegacy} js_::JSObject /* = dart._interceptors::JSObject */]));
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSAny");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSObject");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomTypedArray");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.RenamedJSArray");
let final js_::JSAny? /* = dart.core::Object? */ #t1 = (() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */} in !(#t1 == null);
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,18 @@
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
void test(JSAny any) {}
void main() {}

View file

@ -0,0 +1,17 @@
@JS('library1')
library;
import 'dart:js_interop';
extension type CustomJSAny(JSAny _) implements JSAny {}
@JS('RenamedJSArray')
extension type CustomJSArray(JSArray _) implements JSObject {}
extension type CustomJSObject(JSObject _) implements JSObject {}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
void main() {}
void test(JSAny any) {}

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */ {
lowered final self::CustomJSObject /* = dart._interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = dart._interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = dart._interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = dart._interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,87 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */ {
lowered final self::CustomJSObject /* = dart._interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny? /* = dart.core::Object? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString /* = dart.core::String */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSString? /* = dart.core::String? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSNumber /* = dart.core::double */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBoolean /* = dart.core::bool */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSSymbol /* = dart._interceptors::JavaScriptSymbol */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSBigInt /* = dart._interceptors::JavaScriptBigInt */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSObject? /* = dart._interceptors::JSObject? */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSAny /* = dart.core::Object */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSObject /* = dart._interceptors::JSObject */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */>(any);
js_::JSAnyUtilityExtension|isA<self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */>(any);
js_::JSAnyUtilityExtension|isA<js_::JSAny /* = dart.core::Object */>((() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */});
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -0,0 +1,54 @@
@dart.js_interop::JS::•("library1")
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@js_::JS::•("RenamedJSArray")
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
;
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
;
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
;
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
;
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void
;
static method main() → void
;
Extra constant evaluation status:
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///isa.dart:5:2 -> InstanceConstant(const JS{JS.name: "library1"})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///isa.dart:16:2 -> InstanceConstant(const JS{JS.name: "RenamedJSArray"})
Extra constant evaluation: evaluated: 10, effectively constant: 2

View file

@ -0,0 +1,88 @@
@#C2
library;
import self as self;
import "dart:js_interop" as js_;
import "dart:js_interop_unsafe" as js_2;
import "dart:js_interop";
extension type CustomJSAny(js_::JSAny /* = dart.core::Object */ _) implements js_::JSAny /* = dart.core::Object */ {
abstract extension-type-member representation-field get _() → js_::JSAny /* = dart.core::Object */;
constructor • = self::CustomJSAny|constructor#;
constructor tearoff • = self::CustomJSAny|constructor#_#new#tearOff;
}
extension type CustomJSObject(js_::JSObject /* = dart._interceptors::JSObject */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSObject /* = dart._interceptors::JSObject */;
constructor • = self::CustomJSObject|constructor#;
constructor tearoff • = self::CustomJSObject|constructor#_#new#tearOff;
}
extension type CustomTypedArray(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */;
constructor • = self::CustomTypedArray|constructor#;
constructor tearoff • = self::CustomTypedArray|constructor#_#new#tearOff;
}
@#C4
extension type CustomJSArray(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) implements js_::JSObject /* = dart._interceptors::JSObject */ {
abstract extension-type-member representation-field get _() → js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */;
constructor • = self::CustomJSArray|constructor#;
constructor tearoff • = self::CustomJSArray|constructor#_#new#tearOff;
}
static extension-type-member method CustomJSAny|constructor#(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */ {
lowered final self::CustomJSAny /* = dart.core::Object */ #this = _;
return #this;
}
static extension-type-member method CustomJSAny|constructor#_#new#tearOff(js_::JSAny /* = dart.core::Object */ _) → self::CustomJSAny /* = dart.core::Object */
return self::CustomJSAny|constructor#(_);
static extension-type-member method CustomJSObject|constructor#(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */ {
lowered final self::CustomJSObject /* = dart._interceptors::JSObject */ #this = _;
return #this;
}
static extension-type-member method CustomJSObject|constructor#_#new#tearOff(js_::JSObject /* = dart._interceptors::JSObject */ _) → self::CustomJSObject /* = dart._interceptors::JSObject */
return self::CustomJSObject|constructor#(_);
static extension-type-member method CustomTypedArray|constructor#(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ {
lowered final self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */ #this = _;
return #this;
}
static extension-type-member method CustomTypedArray|constructor#_#new#tearOff(js_::JSTypedArray /* = dart.typed_data.implementation::NativeTypedData */ _) → self::CustomTypedArray /* = dart.typed_data.implementation::NativeTypedData */
return self::CustomTypedArray|constructor#(_);
static extension-type-member method CustomJSArray|constructor#(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ {
lowered final self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */ #this = _;
return #this;
}
static extension-type-member method CustomJSArray|constructor#_#new#tearOff(js_::JSArray<js_::JSAny? /* = dart.core::Object? */> /* = dart._interceptors::JSArray<dart.core::Object?> */ _) → self::CustomJSArray /* = dart._interceptors::JSArray<dart.core::Object?> */
return self::CustomJSArray|constructor#(_);
static method test(js_::JSAny /* = dart.core::Object */ any) → void {
!(any == null);
true;
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "string");
any == null || js_::JSAnyUtilityExtension|typeofEquals(any, "string");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "number");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "boolean");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "symbol");
!(any == null) && js_::JSAnyUtilityExtension|typeofEquals(any, "bigint");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
any == null || js_::JSAnyUtilityExtension|instanceOfString(any, "Object");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "Array");
!(any == null) && js_::JSAnyUtilityExtension|instanceof(any, js_2::JSObjectUnsafeUtilExtension|callMethodVarArgs<js_::JSFunction /* = dart._interceptors::JavaScriptFunction */>(js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Object") as{ForLegacy} js_::JSObject /* = dart._interceptors::JSObject */, js_::StringToJSString|get#toJS("getPrototypeOf"), <js_::JSAny? /* = dart.core::Object? */>[js_2::JSObjectUnsafeUtilExtension|[](js_::globalContext, "Int8Array") as{ForLegacy} js_::JSObject /* = dart._interceptors::JSObject */]));
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSAny");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomJSObject");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.CustomTypedArray");
!(any == null) && js_::JSAnyUtilityExtension|instanceOfString(any, "library1.RenamedJSArray");
let final js_::JSAny? /* = dart.core::Object? */ #t1 = (() → js_::JSAny /* = dart.core::Object */ {
return any;
})(){() → js_::JSAny /* = dart.core::Object */} in !(#t1 == null);
}
static method main() → void {}
constants {
#C1 = "library1"
#C2 = js_::JS {name:#C1}
#C3 = "RenamedJSArray"
#C4 = js_::JS {name:#C3}
}
Constructor coverage from constants:
org-dartlang-testcase:///isa.dart:
- JS. (from org-dartlang-sdk:///lib/js_interop/js_interop.dart)
- Object. (from org-dartlang-sdk:///lib/core/object.dart)

View file

@ -43,6 +43,11 @@ extension JSAnyUtilityExtension on JSAny? {
bool instanceof(JSFunction constructor) =>
foreign_helper.JS('bool', '# instanceof #', this, constructor);
@patch
bool isA<T>() => throw UnimplementedError(
"This should never be called. Calls to 'isA' should have been "
'transformed by the interop transformer.');
@patch
@pragma('dart2js:prefer-inline')
Object? dartify() => js_util.dartify(this);

View file

@ -61,6 +61,11 @@ extension JSAnyUtilityExtension on JSAny? {
'(o, c) => o instanceof c', toExternRef, constructor.toExternRef)
.toBool();
@patch
bool isA<T>() => throw UnimplementedError(
"This should never be called. Calls to 'isA' should have been "
'transformed by the interop transformer.');
@patch
Object? dartify() => js_util.dartify(this);
}
@ -90,8 +95,8 @@ extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
extension FunctionToJSExportedDartFunction on Function {
@patch
JSExportedDartFunction get toJS => throw UnimplementedError(
'This should never be called. Calls to toJS should have been transformed '
'by the interop transformer.');
"This should never be called. Calls to 'toJS' should have been "
'transformed by the interop transformer.');
}
/// Embedded global property for wrapped Dart objects passed via JS interop.

View file

@ -19,6 +19,7 @@
/// {@category Web}
library dart.js_interop;
import "dart:_internal" show Since;
import 'dart:_js_types';
import 'dart:js_interop_unsafe';
import 'dart:typed_data';
@ -73,6 +74,7 @@ extension type JSAny._(JSAnyRepType _jsAny) implements Object {}
/// This is the supertype of all JS objects, but not other JS types, like
/// primitives. See https://dart.dev/web/js-interop for more details on how to
/// use JS interop.
@JS('Object')
extension type JSObject._(JSObjectRepType _jsObject) implements JSAny {
/// Constructor to go from an object from previous interop, like the types
/// from `package:js` or `dart:html`, to [JSObject].
@ -91,12 +93,14 @@ extension type JSObject._(JSObjectRepType _jsObject) implements JSAny {
external JSObjectRepType _createObjectLiteral();
/// The type of all JS functions.
@JS('Function')
extension type JSFunction._(JSFunctionRepType _jsFunction)
implements JSObject {}
/// The type of all Dart functions adapted to be callable from JS. We only allow
/// a subset of Dart functions to be callable from JS.
// TODO(joshualitt): Detail exactly what are the requirements.
@JS('Function')
extension type JSExportedDartFunction._(
JSExportedDartFunctionRepType _jsExportedDartFunction)
implements JSFunction {}
@ -143,14 +147,17 @@ extension type JSPromise<T extends JSAny?>._(JSPromiseRepType _jsPromise)
/// The type of the boxed Dart object that can be passed to JS safely. There is
/// no interface specified of this boxed object, and you may get a new box each
/// time you box the same Dart object.
@JS('Object')
extension type JSBoxedDartObject._(JSBoxedDartObjectRepType _jsBoxedDartObject)
implements JSObject {}
/// The type of JS' `ArrayBuffer`.
@JS('ArrayBuffer')
extension type JSArrayBuffer._(JSArrayBufferRepType _jsArrayBuffer)
implements JSObject {}
/// The type of JS' `DataView`.
@JS('DataView')
extension type JSDataView._(JSDataViewRepType _jsDataView)
implements JSObject {}
@ -159,38 +166,47 @@ extension type JSTypedArray._(JSTypedArrayRepType _jsTypedArray)
implements JSObject {}
/// The type of JS' `Int8Array`.
@JS('Int8Array')
extension type JSInt8Array._(JSInt8ArrayRepType _jsInt8Array)
implements JSTypedArray {}
/// The type of JS' `Uint8Array`.
@JS('Uint8Array')
extension type JSUint8Array._(JSUint8ArrayRepType _jsUint8Array)
implements JSTypedArray {}
/// The type of JS' `Uint8ClampedArray`.
@JS('Uint8ClampedArray')
extension type JSUint8ClampedArray._(
JSUint8ClampedArrayRepType _jsUint8ClampedArray) implements JSTypedArray {}
/// The type of JS' `Int16Array`.
@JS('Int16Array')
extension type JSInt16Array._(JSInt16ArrayRepType _jsInt16Array)
implements JSTypedArray {}
/// The type of JS' `Uint16Array`.
@JS('Uint16Array')
extension type JSUint16Array._(JSUint16ArrayRepType _jsUint16Array)
implements JSTypedArray {}
/// The type of JS' `Int32Array`.
@JS('Int32Array')
extension type JSInt32Array._(JSInt32ArrayRepType _jsInt32Array)
implements JSTypedArray {}
/// The type of JS' `Uint32Array`.
@JS('Uint32Array')
extension type JSUint32Array._(JSUint32ArrayRepType _jsUint32Array)
implements JSTypedArray {}
/// The type of JS' `Float32Array`.
@JS('Float32Array')
extension type JSFloat32Array._(JSFloat32ArrayRepType _jsFloat32Array)
implements JSTypedArray {}
/// The type of JS' `Float64Array`.
@JS('Float64Array')
extension type JSFloat64Array._(JSFloat64ArrayRepType _jsFloat64Array)
implements JSTypedArray {}
@ -258,13 +274,62 @@ extension JSAnyUtilityExtension on JSAny? {
/// Returns whether this [JSAny]? is an `instanceof` [constructor].
external bool instanceof(JSFunction constructor);
/// Like [instanceof], but only takes a [String] for the constructor name,
/// which is then looked up in the [globalContext].
/// Returns whether this [JSAny]? is an `instanceof` the constructor that is
/// defined by [constructorName], which is looked up in the [globalContext].
///
/// If [constructorName] contains '.'s, we split the name into several parts
/// in order to get the constructor. For example, `library1.JSClass` would
/// involve fetching `library1` off of the [globalContext], and then fetching
/// `JSClass` off of `library1` to get the constructor.
///
/// If [constructorName] is empty or any of the parts or the constructor don't
/// exist, returns false.
bool instanceOfString(String constructorName) {
final constructor = globalContext[constructorName] as JSFunction?;
return constructor != null && instanceof(constructor);
if (constructorName.isEmpty) return false;
final parts = constructorName.split('.');
JSObject? constructor = globalContext;
for (final part in parts) {
constructor = constructor?[part] as JSObject?;
if (constructor == null) return false;
}
return instanceof(constructor as JSFunction);
}
/// Returns whether this [JSAny]? is the actual JS type that is declared by
/// [T].
///
/// This method uses a combination of null, `typeof`, and `instanceof` checks
/// in order to do this check. Use this instead of `is` checks.
///
/// If [T] is a primitive JS type e.g. [JSString], this uses a `typeof` check
/// that corresponds to that primitive type e.g. `typeofEquals('string')`.
///
/// If [T] is a non-primitive JS type e.g. [JSArray] or an interop extension
/// type on one, this uses an `instanceof` check using the name or @[JS]
/// rename of the given type e.g. `instanceOfString('Array')`. Note that if
/// you rename the library using the @[JS] annotation, this uses the rename in
/// the `instanceof` check e.g. `instanceOfString('library1.JSClass')`.
///
/// In order to determine the right value for the `instanceof` check, this
/// uses the name or the rename of the extension type. So, if you had an
/// interop extension type `JSClass` that wraps `JSArray`, this does an
/// `instanceOfString('JSClass')` check and not an `instanceOfString('Array')`
/// check.
///
/// There are two expeptions to this rule. The first exception is
/// `JSTypedArray`. As `TypedArray` does not exist as a property in JS, this
/// does some prototype checking to make `isA<JSTypedArray>` do the right
/// thing. The other exception is `JSAny`. If you do a `isA<JSAny>` check, it
/// will only do a null-check.
///
/// Using this method with a [T] that has an object literal constructor will
/// result in an error as you likely want to use [JSObject] instead.
///
/// Using this method with a [T] that wraps a primitive JS type will result in
/// an error telling you to use the primitive JS type instead.
@Since('3.4')
external bool isA<T extends JSAny?>();
/// Effectively the inverse of [jsify], [dartify] Takes a JavaScript object,
/// and converts it to a Dart based object. Only JS primitives, arrays, or
/// 'map' like JS objects are supported.

View file

@ -0,0 +1,274 @@
// Copyright (c) 2024, 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 `dart:js_interop`'s `isA` method.
import 'dart:js_interop';
import 'dart:typed_data';
import 'package:expect/expect.dart';
@JS()
external void eval(String code);
extension type ObjectLiteral._(JSObject _) implements JSObject {
external ObjectLiteral({int a});
@JS('toString')
external JSFunction get toStr;
}
extension type CustomJSAny(JSAny _) implements JSAny {}
extension type Date._(JSObject _) implements JSObject {
external Date();
}
extension type CustomTypedArray(JSTypedArray _) implements JSObject {}
@JS('SubtypeArray')
extension type ArraySubtype._(JSArray _) implements JSObject {
external ArraySubtype();
}
@JS()
external JSBigInt BigInt(String val);
@JS()
external JSSymbol Symbol(String val);
void testIsJSTypedArray(JSTypedArray any) {
Expect.isTrue(any.isA<JSTypedArray>());
Expect.isTrue(any.isA<JSTypedArray?>());
testIsJSObject(any);
}
void testIsJSObject(JSObject any) {
Expect.isTrue(any.isA<JSObject>());
Expect.isTrue(any.isA<JSObject?>());
testIsJSAny(any);
}
void testIsJSAny(JSAny any) {
Expect.isTrue(any.isA<JSAny>());
Expect.isTrue(any.isA<JSAny?>());
}
void main() {
// Null.
JSAny? nil = null;
Expect.isTrue(nil.isA<JSString?>());
Expect.isTrue(nil.isA<JSObject?>());
Expect.isTrue(nil.isA<JSAny?>());
Expect.isFalse(nil.isA<JSString>());
Expect.isFalse(nil.isA<JSObject>());
Expect.isFalse(nil.isA<JSAny>());
// Test inheritance chain, nullability, and one false case.
// Primitives.
final jsString = ''.toJS;
Expect.isTrue(jsString.isA<JSString>());
Expect.isTrue(jsString.isA<JSString?>());
testIsJSAny(jsString);
Expect.isFalse(jsString.isA<JSSymbol>());
final jsNum = 0.toJS;
Expect.isTrue(jsNum.isA<JSNumber>());
Expect.isTrue(jsNum.isA<JSNumber?>());
testIsJSAny(jsNum);
Expect.isFalse(jsNum.isA<JSString>());
final jsBool = true.toJS;
Expect.isTrue(jsBool.isA<JSBoolean>());
Expect.isTrue(jsBool.isA<JSBoolean?>());
testIsJSAny(jsBool);
Expect.isFalse(jsBool.isA<JSNumber>());
final jsBigInt = BigInt('0');
Expect.isTrue(jsBigInt.isA<JSBigInt>());
Expect.isTrue(jsBigInt.isA<JSBigInt?>());
testIsJSAny(jsBigInt);
Expect.isFalse(jsBigInt.isA<JSBoolean>());
final jsSymbol = Symbol('symbol');
Expect.isTrue(jsSymbol.isA<JSSymbol>());
Expect.isTrue(jsSymbol.isA<JSSymbol?>());
testIsJSAny(jsSymbol);
Expect.isFalse(jsSymbol.isA<JSBigInt>());
// Objects.
final jsObject = ObjectLiteral(a: 0);
testIsJSObject(jsObject);
Expect.isFalse(jsObject.isA<JSBoolean>());
// Note that this is true even though it's a supertype - we can't
// differentiate between the two types. This is okay because users will get a
// consistent error when trying to internalize it.
Expect.isTrue(jsObject.isA<JSBoxedDartObject>());
final jsBox = ''.toJSBox;
Expect.isTrue(jsBox.isA<JSBoxedDartObject>());
Expect.isTrue(jsBox.isA<JSBoxedDartObject?>());
testIsJSObject(jsBox);
Expect.isFalse(jsBox.isA<JSString>());
final jsArray = <JSAny?>[].toJS;
Expect.isTrue(jsArray.isA<JSArray>());
Expect.isTrue(jsArray.isA<JSArray?>());
// Can't differentiate generics.
Expect.isTrue(jsArray.isA<JSArray<JSNumber>>());
testIsJSObject(jsArray);
Expect.isFalse(jsArray.isA<JSFunction>());
final jsPromise = Future.delayed(Duration.zero).toJS;
Expect.isTrue(jsPromise.isA<JSPromise>());
Expect.isTrue(jsPromise.isA<JSPromise?>());
// Can't differentiate generics.
Expect.isTrue(jsPromise.isA<JSPromise<JSNumber>>());
testIsJSObject(jsPromise);
Expect.isFalse(jsPromise.isA<JSTypedArray>());
final jsFunction = jsObject.toStr;
Expect.isTrue(jsFunction.isA<JSFunction?>());
Expect.isTrue(jsFunction.isA<JSFunction>());
testIsJSObject(jsFunction);
Expect.isFalse(jsFunction.isA<JSNumber>());
// Note that this is true even though it's a supertype - we can't
// differentiate between the two types. This is okay because users will get a
// consistent error when trying to internalize it.
Expect.isTrue(jsFunction.isA<JSExportedDartFunction>());
final jsExportedDartFunction = () {}.toJS;
Expect.isTrue(jsExportedDartFunction.isA<JSExportedDartFunction>());
Expect.isTrue(jsExportedDartFunction.isA<JSExportedDartFunction?>());
Expect.isTrue(jsExportedDartFunction.isA<JSFunction>());
testIsJSObject(jsExportedDartFunction);
Expect.isFalse(jsExportedDartFunction.isA<JSSymbol>());
// Typed data.
final jsArrayBuffer = Uint8List(1).buffer.toJS;
Expect.isTrue(jsArrayBuffer.isA<JSArrayBuffer>());
Expect.isTrue(jsArrayBuffer.isA<JSArrayBuffer?>());
Expect.isFalse(jsArrayBuffer.isA<JSTypedArray>());
testIsJSObject(jsArrayBuffer);
Expect.isFalse(jsArrayBuffer.isA<JSArray>());
final jsDataView = ByteData(1).toJS;
Expect.isTrue(jsDataView.isA<JSDataView>());
Expect.isTrue(jsDataView.isA<JSDataView?>());
Expect.isFalse(jsDataView.isA<JSTypedArray>());
testIsJSObject(jsDataView);
Expect.isFalse(jsDataView.isA<JSArrayBuffer>());
final jsUint8Array = Uint8List(1).toJS;
Expect.isTrue(jsUint8Array.isA<JSUint8Array>());
Expect.isTrue(jsUint8Array.isA<JSUint8Array?>());
testIsJSTypedArray(jsUint8Array);
Expect.isFalse(jsUint8Array.isA<JSDataView>());
final jsUint16Array = Uint16List(1).toJS;
Expect.isTrue(jsUint16Array.isA<JSUint16Array>());
Expect.isTrue(jsUint16Array.isA<JSUint16Array?>());
testIsJSTypedArray(jsUint16Array);
Expect.isFalse(jsUint16Array.isA<JSUint8Array>());
final jsUint32Array = Uint32List(1).toJS;
Expect.isTrue(jsUint32Array.isA<JSUint32Array>());
Expect.isTrue(jsUint32Array.isA<JSUint32Array?>());
testIsJSTypedArray(jsUint32Array);
Expect.isFalse(jsUint32Array.isA<JSUint16Array>());
final jsUint8ClampedArray = Uint8ClampedList(1).toJS;
Expect.isTrue(jsUint8ClampedArray.isA<JSUint8ClampedArray>());
Expect.isTrue(jsUint8ClampedArray.isA<JSUint8ClampedArray?>());
testIsJSTypedArray(jsUint8ClampedArray);
Expect.isFalse(jsUint8ClampedArray.isA<JSUint32Array>());
final jsInt8Array = Int8List(1).toJS;
Expect.isTrue(jsInt8Array.isA<JSInt8Array>());
Expect.isTrue(jsInt8Array.isA<JSInt8Array?>());
testIsJSTypedArray(jsInt8Array);
Expect.isFalse(jsInt8Array.isA<JSUint8ClampedArray>());
final jsInt16Array = Int16List(1).toJS;
Expect.isTrue(jsInt16Array.isA<JSInt16Array>());
Expect.isTrue(jsInt16Array.isA<JSInt16Array?>());
testIsJSTypedArray(jsInt16Array);
Expect.isFalse(jsInt16Array.isA<JSInt8Array>());
final jsInt32Array = Int32List(1).toJS;
Expect.isTrue(jsInt32Array.isA<JSInt32Array>());
Expect.isTrue(jsInt32Array.isA<JSInt32Array?>());
testIsJSTypedArray(jsInt32Array);
Expect.isFalse(jsInt32Array.isA<JSInt16Array>());
final jsFloat32Array = Float32List(1).toJS;
Expect.isTrue(jsFloat32Array.isA<JSFloat32Array>());
Expect.isTrue(jsFloat32Array.isA<JSFloat32Array?>());
testIsJSTypedArray(jsFloat32Array);
Expect.isFalse(jsFloat32Array.isA<JSInt32Array>());
final jsFloat64Array = Float64List(1).toJS;
Expect.isTrue(jsFloat64Array.isA<JSFloat64Array>());
Expect.isTrue(jsFloat64Array.isA<JSFloat64Array?>());
testIsJSTypedArray(jsFloat64Array);
Expect.isFalse(jsFloat64Array.isA<JSFloat32Array>());
// User types. If they are a subtype of `JSObject`, we will use the
// declaration name with any renaming. If not, we will use the core type.
eval('''
class SubtypeArray extends Array {}
globalThis.SubtypeArray = SubtypeArray;
''');
final customJsAny = CustomJSAny(jsArray);
Expect.isFalse(customJsAny.isA<CustomJSAny>());
Expect.isFalse(customJsAny.isA<CustomJSAny?>());
Expect.isFalse(jsArray.isA<CustomJSAny>());
Expect.isFalse(jsArray.isA<CustomJSAny?>());
Expect.isTrue(nil.isA<CustomJSAny?>());
Expect.isFalse(nil.isA<CustomJSAny>());
Expect.isTrue(customJsAny.isA<JSArray>());
Expect.isTrue(customJsAny.isA<JSArray?>());
final date = Date();
Expect.isTrue(date.isA<Date>());
Expect.isTrue(date.isA<Date?>());
Expect.isFalse(jsObject.isA<Date>());
Expect.isFalse(jsObject.isA<Date?>());
Expect.isTrue(nil.isA<Date?>());
Expect.isFalse(nil.isA<Date>());
Expect.isTrue(date.isA<JSObject>());
Expect.isTrue(date.isA<JSObject?>());
final customTypedArray = CustomTypedArray(jsUint8Array);
Expect.isFalse(customTypedArray.isA<CustomTypedArray>());
Expect.isFalse(customTypedArray.isA<CustomTypedArray?>());
Expect.isFalse(jsUint8Array.isA<CustomTypedArray>());
Expect.isFalse(jsUint8Array.isA<CustomTypedArray?>());
Expect.isTrue(nil.isA<CustomTypedArray?>());
Expect.isFalse(nil.isA<CustomTypedArray>());
Expect.isTrue(customTypedArray.isA<JSTypedArray>());
Expect.isTrue(customTypedArray.isA<JSTypedArray?>());
final arraySubtype = ArraySubtype();
Expect.isTrue(arraySubtype.isA<ArraySubtype>());
Expect.isTrue(arraySubtype.isA<ArraySubtype?>());
Expect.isFalse(jsArray.isA<ArraySubtype>());
Expect.isFalse(jsArray.isA<ArraySubtype?>());
Expect.isTrue(nil.isA<ArraySubtype?>());
Expect.isFalse(nil.isA<ArraySubtype>());
Expect.isTrue(arraySubtype.isA<JSArray>());
Expect.isTrue(arraySubtype.isA<JSArray?>());
// Make sure we recurse and don't reevaluate the receiver.
var count = 0;
Expect.isTrue((() {
Expect.isTrue(jsObject.isA<JSObject>());
count++;
return jsObject;
})()
.isA<JSObject>());
Expect.equals(count, 1);
}

View file

@ -0,0 +1,54 @@
// Copyright (c) 2024, 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 `dart:js_interop`'s `isA` method with library renames.
@JS('library1.library2')
library;
import 'dart:js_interop';
import 'package:expect/expect.dart';
import 'functional_test.dart' as functional_test;
extension type Date._(JSObject _) implements JSObject {
external Date();
}
@JS('Date')
extension type RenamedDate._(JSObject _) implements JSObject {
external RenamedDate();
}
void main() {
functional_test.eval('''
globalThis.library1 = {};
globalThis.library1.library2 = {};
globalThis.library1.library2.Date = function Date() {}
''');
final date = Date();
final unscopedDate = functional_test.Date();
Expect.isTrue(date.isA<Date>());
Expect.isTrue(date.isA<Date?>());
Expect.isFalse(unscopedDate.isA<Date>());
Expect.isFalse(unscopedDate.isA<Date?>());
Expect.isFalse(date.isA<functional_test.Date>());
Expect.isFalse(date.isA<functional_test.Date?>());
Expect.isTrue(date.isA<JSObject>());
Expect.isTrue(date.isA<JSObject?>());
final renamedDate = RenamedDate();
Expect.isTrue(renamedDate.isA<RenamedDate>());
Expect.isTrue(renamedDate.isA<RenamedDate?>());
Expect.isTrue(date.isA<RenamedDate>());
Expect.isTrue(date.isA<RenamedDate?>());
Expect.isTrue(renamedDate.isA<Date>());
Expect.isTrue(renamedDate.isA<Date?>());
Expect.isFalse(unscopedDate.isA<RenamedDate>());
Expect.isFalse(unscopedDate.isA<RenamedDate?>());
Expect.isFalse(renamedDate.isA<functional_test.Date>());
Expect.isFalse(renamedDate.isA<functional_test.Date?>());
}

View file

@ -0,0 +1,59 @@
// Copyright (c) 2024, 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 `dart:js_interop`'s `isA` method returns the right errors.
import 'dart:js_interop';
extension type NonLiteral._(JSObject o) implements JSObject {}
extension type ObjectLiteral._(JSObject o) implements JSObject {
external ObjectLiteral({int a});
}
extension type BothConstructors._(JSObject o) implements JSObject {
external BothConstructors({int a});
external BothConstructors.fact(int a);
}
extension type WrapObjectLiteral._(ObjectLiteral o) implements ObjectLiteral {}
extension type CustomJSString(JSString _) implements JSString {}
void test<T extends JSAny?, U extends NonLiteral>(JSAny? any) {
any.isA<T>();
// ^
// [web] Type argument 'T' needs to be an interop 'ExtensionType'.
any.isA<U>();
// ^
// [web] Type argument 'U' needs to be an interop 'ExtensionType'.
final tearoff = 0.toJS.isA;
// ^
// [web] 'isA' can't be torn off.
tearoff<JSAny>();
final tearoffWithTypeParam = 0.toJS.isA<JSNumber>;
// ^
// [web] 'isA' can't be torn off.
tearoffWithTypeParam();
final tearoffWithGenericTypeParam = 0.toJS.isA<T>;
// ^
// [web] 'isA' can't be torn off.
tearoffWithGenericTypeParam();
any.isA<NonLiteral>();
any.isA<ObjectLiteral>();
// ^
// [web] Type argument 'ObjectLiteral' has an object literal constructor. Because 'isA' uses the type's name or '@JS()' rename, this may result in an incorrect type check.
any.isA<BothConstructors>();
// ^
// [web] Type argument 'BothConstructors' has an object literal constructor. Because 'isA' uses the type's name or '@JS()' rename, this may result in an incorrect type check.
any.isA<WrapObjectLiteral>();
CustomJSString(''.toJS).isA<CustomJSString>();
// ^
// [web] Type argument 'CustomJSString' wraps primitive JS type 'JSString', which is specially handled using 'typeof'.
}
void main() {}

View file

@ -85,6 +85,8 @@ js/static_interop_test/external_extension_members_test: SkipByDesign # Issue 420
js/static_interop_test/external_static_member_lowerings_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/external_static_member_lowerings_trusttypes_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/external_static_member_lowerings_with_namespaces_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/isa/functional_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/isa/library_renaming_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_array_proxy_or_ref_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_array_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code
js/static_interop_test/js_function_conversions_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code