[dart:js_util] Handle type parameters in export/mocks.
Several changes are made:
- createDartExport now does not export methods that define type parameters.
- createStaticInteropMock adds conformance checks to make sure implementing
members can handle all possible values of a type parameter in an interop
member. An error is added to reduce confusion around this.
- Export creator now uses dart:js_interop_unsafe for a lot of its lowering
as the dart:js_util equivalents are buggy when it comes to calling exported
functions in JS with JS types.
- Small code changes are added to backends to handle the above changes.
Change-Id: Ie3b6b157930537267f270b60373b2b17e0a14344
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316141
Reviewed-by: Joshua Litt <joshualitt@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index fdab0d0..b738057 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -7803,7 +7803,7 @@
name)> templateJsInteropExportDisallowedMember = const Template<
Message Function(String name)>(
problemMessageTemplate:
- r"""Member '#name' is not a concrete instance member, and therefore can't be exported.""",
+ r"""Member '#name' is not a concrete instance member or declares type parameters, and therefore can't be exported.""",
correctionMessageTemplate:
r"""Remove the `@JSExport` annotation from the member, and use an instance member to call this member instead.""",
withArguments: _withArgumentsJsInteropExportDisallowedMember);
@@ -7820,7 +7820,7 @@
name = demangleMixinApplicationName(name);
return new Message(codeJsInteropExportDisallowedMember,
problemMessage:
- """Member '${name}' is not a concrete instance member, and therefore can't be exported.""",
+ """Member '${name}' is not a concrete instance member or declares type parameters, and therefore can't be exported.""",
correctionMessage: """Remove the `@JSExport` annotation from the member, and use an instance member to call this member instead.""",
arguments: {'name': name});
}
diff --git a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
index a838f43..8492c41 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/export_checker.dart
@@ -258,13 +258,15 @@
}
extension ProcedureExtension on Procedure {
- // We only care about concrete instance procedures.
+ // We only care about concrete instance procedures that don't define their own
+ // type parameters.
bool get exportable =>
!isAbstract &&
!isStatic &&
!isExtensionMember &&
!isFactory &&
!isExternal &&
+ function.typeParameters.isEmpty &&
kind != ProcedureKind.Operator;
}
diff --git a/pkg/_js_interop_checks/lib/src/transformations/export_creator.dart b/pkg/_js_interop_checks/lib/src/transformations/export_creator.dart
index a6f8d6c..f6f7545 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/export_creator.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/export_creator.dart
@@ -20,68 +20,76 @@
import 'static_interop_mock_validator.dart';
class ExportCreator extends Transformer {
- final Procedure _allowInterop;
+ final Procedure _callMethodVarArgs;
final Procedure _createDartExport;
final Procedure _createStaticInteropMock;
final JsInteropDiagnosticReporter _diagnosticReporter;
final ExportChecker _exportChecker;
- final InterfaceType _functionType;
+ final Procedure _functionToJS;
final Procedure _getProperty;
- final Procedure _globalThis;
- final InterfaceType _objectType;
+ final Procedure _globalJSObject;
+ final Class _jsAny;
+ final Class _jsObject;
final Procedure _setProperty;
+ final Procedure _stringToJS;
final StaticInteropMockValidator _staticInteropMockValidator;
final TypeEnvironment _typeEnvironment;
ExportCreator(
this._typeEnvironment, this._diagnosticReporter, this._exportChecker)
- : _allowInterop = _typeEnvironment.coreTypes.index
- .getTopLevelProcedure('dart:js_util', 'allowInterop'),
+ : _callMethodVarArgs = _typeEnvironment.coreTypes.index
+ .getTopLevelProcedure('dart:js_interop_unsafe',
+ 'JSObjectUtilExtension|callMethodVarArgs'),
_createDartExport = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createDartExport'),
_createStaticInteropMock = _typeEnvironment.coreTypes.index
.getTopLevelProcedure('dart:js_util', 'createStaticInteropMock'),
- _functionType = _typeEnvironment.coreTypes.functionNonNullableRawType,
- _getProperty = (_typeEnvironment.coreTypes.index.tryGetTopLevelMember(
- 'dart:js_util', '_getPropertyTrustType') ??
- _typeEnvironment.coreTypes.index.getTopLevelProcedure(
- 'dart:js_util', 'getProperty')) as Procedure,
- _globalThis = _typeEnvironment.coreTypes.index
- .getTopLevelProcedure('dart:js_util', 'get:globalThis'),
- _objectType = _typeEnvironment.coreTypes.objectNonNullableRawType,
- _setProperty = (_typeEnvironment.coreTypes.index.tryGetTopLevelMember(
- 'dart:js_util', '_setPropertyUnchecked') ??
- _typeEnvironment.coreTypes.index.getTopLevelProcedure(
- 'dart:js_util', 'setProperty')) as Procedure,
+ _functionToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
+ 'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
+ _getProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
+ 'dart:js_interop_unsafe', 'JSObjectUtilExtension|[]'),
+ _globalJSObject = _typeEnvironment.coreTypes.index
+ .getTopLevelProcedure('dart:js_interop', 'get:globalJSObject'),
+ _jsAny = _typeEnvironment.coreTypes.index
+ .getClass('dart:_js_types', 'JSAny'),
+ _jsObject = _typeEnvironment.coreTypes.index
+ .getClass('dart:_js_types', 'JSObject'),
+ _setProperty = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
+ 'dart:js_interop_unsafe', 'JSObjectUtilExtension|[]='),
+ _stringToJS = _typeEnvironment.coreTypes.index.getTopLevelProcedure(
+ 'dart:js_interop', 'StringToJSString|get#toJS'),
_staticInteropMockValidator = StaticInteropMockValidator(
_diagnosticReporter, _exportChecker, _typeEnvironment);
@override
TreeNode visitStaticInvocation(StaticInvocation node) {
if (node.target == _createDartExport) {
- var typeArguments = node.arguments.types;
+ final typeArguments = node.arguments.types;
assert(typeArguments.length == 1);
if (_verifyExportable(node, typeArguments[0])) {
return _createExport(node, typeArguments[0] as InterfaceType);
}
} else if (node.target == _createStaticInteropMock) {
- var typeArguments = node.arguments.types;
+ final typeArguments = node.arguments.types;
assert(typeArguments.length == 2);
- var staticInteropType = typeArguments[0];
- var dartType = typeArguments[1];
+ final staticInteropType = typeArguments[0];
+ final dartType = typeArguments[1];
- var exportable = _verifyExportable(node, dartType);
- var staticInteropTypeArgumentCorrect = _staticInteropMockValidator
+ 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)) {
- var arguments = node.arguments.positional;
+ final arguments = node.arguments.positional;
assert(arguments.length == 1 || arguments.length == 2);
- var proto = arguments.length == 2 ? arguments[1] : null;
+ final proto = arguments.length == 2 ? arguments[1] : null;
return _createExport(node, dartType, staticInteropType, proto);
}
@@ -164,6 +172,48 @@
/// and returns it.
TreeNode _createExport(StaticInvocation node, InterfaceType dartType,
[DartType? returnType, Expression? proto]) {
+ Expression asJSObject(Expression object, [bool nullable = false]) =>
+ AsExpression(
+ object,
+ InterfaceType(_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: InterfaceType(_jsAny, Nullability.nullable))
+ ], types: [
+ returnType
+ ]))
+ ..fileOffset = node.fileOffset;
+ }
+
+ // Get the global 'Object' property.
+ Expression getObjectProperty() => asJSObject(StaticInvocation(_getProperty,
+ Arguments([StaticGet(_globalJSObject), toJSString('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)],
+ InterfaceType(_jsObject, Nullability.nonNullable));
+ }
+
var exportMap =
_exportChecker.exportClassToMemberMap[dartType.classNode.reference]!;
@@ -178,23 +228,9 @@
..parent = node.parent;
block.add(dartInstance);
- // Get the global 'Object' property.
- StaticInvocation getObjectProperty() => StaticInvocation(
- _getProperty,
- Arguments([StaticGet(_globalThis), StringLiteral('Object')],
- types: [_objectType]));
-
- // Get a fresh object literal, using the proto to create it if one was
- // given.
- StaticInvocation getLiteral([Expression? proto]) {
- return _callMethod(getObjectProperty(), StringLiteral('create'),
- [proto ?? NullLiteral()], _objectType);
- }
-
var jsExporter = VariableDeclaration('#jsExporter',
- initializer: AsExpression(getLiteral(proto), returnType)
- ..fileOffset = node.fileOffset,
- type: returnType,
+ initializer: getLiteral(proto),
+ type: InterfaceType(_jsObject, Nullability.nonNullable),
isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
@@ -202,13 +238,11 @@
for (var exportName in exportMap.keys) {
var exports = exportMap[exportName]!;
- ExpressionStatement setProperty(VariableGet jsObject, String propertyName,
- StaticInvocation wrappedValue) {
- // `setProperty(jsObject, propertyName, wrappedValue)`
- return ExpressionStatement(StaticInvocation(
- _setProperty,
- Arguments([jsObject, StringLiteral(propertyName), wrappedValue],
- types: [_objectType])))
+ ExpressionStatement setProperty(
+ VariableGet jsObject, String propertyName, StaticInvocation jsValue) {
+ // `jsObject[propertyName.toJS] = jsValue`
+ return ExpressionStatement(StaticInvocation(_setProperty,
+ Arguments([jsObject, toJSString(propertyName), jsValue])))
..fileOffset = node.fileOffset
..parent = node.parent;
}
@@ -217,19 +251,19 @@
// With methods, there's only one export per export name.
if (firstExport is Procedure &&
firstExport.kind == ProcedureKind.Method) {
- // `setProperty(jsMock, jsName, allowInterop(dartMock.tearoffMethod))`
+ // `jsExport[jsName.toJS] = dartMock.tearoffMethod.toJS`
block.add(setProperty(
VariableGet(jsExporter),
exportName,
StaticInvocation(
- _allowInterop,
+ _functionToJS,
Arguments([
InstanceTearOff(InstanceAccessKind.Instance,
VariableGet(dartInstance), firstExport.name,
interfaceTarget: firstExport,
- resultType: firstExport.getterType)
- ], types: [
- _functionType
+ resultType: _staticInteropMockValidator
+ .typeParameterResolver
+ .resolve(firstExport.getterType))
]))));
} else {
// Create the mapping from `get` and `set` to their `dartInstance` calls
@@ -242,17 +276,17 @@
// The AST code looks like:
//
// ```
- // setProperty(getSetMap, 'get', allowInterop(() {
+ // getSetMap['get'.toJS] = () {
// return dartInstance.getter;
- // }));
+ // }.toJS;
// ```
//
// in the case of a getter and:
//
// ```
- // setProperty(getSetMap, 'set', allowInterop((val) {
+ // getSetMap['set'.toJS] = (val) {
// dartInstance.setter = val;
- // }));
+ // }.toJS;
// ```
//
// in the case of a setter.
@@ -260,7 +294,9 @@
// 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: _objectType, isSynthesized: true)
+ initializer: getLiteral(),
+ type: InterfaceType(_jsObject, Nullability.nonNullable),
+ isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(getSetMap);
@@ -272,28 +308,30 @@
VariableGet(getSetMap),
'get',
StaticInvocation(
- _allowInterop,
+ _functionToJS,
Arguments([
FunctionExpression(FunctionNode(ReturnStatement(InstanceGet(
InstanceAccessKind.Instance,
VariableGet(dartInstance),
getter.name,
interfaceTarget: getter,
- resultType: getter.getterType))))
- ], types: [
- _functionType
+ resultType: _staticInteropMockValidator
+ .typeParameterResolver
+ .resolve(getter.getterType)))))
]))));
}
if (setter != null) {
var setterParameter = VariableDeclaration('#val',
- type: setter.setterType, isSynthesized: true)
+ type: _staticInteropMockValidator.typeParameterResolver
+ .resolve(setter.setterType),
+ isSynthesized: true)
..fileOffset = node.fileOffset
..parent = node.parent;
block.add(setProperty(
VariableGet(getSetMap),
'set',
StaticInvocation(
- _allowInterop,
+ _functionToJS,
Arguments([
FunctionExpression(FunctionNode(
ExpressionStatement(InstanceSet(
@@ -303,19 +341,17 @@
VariableGet(setterParameter),
interfaceTarget: setter)),
positionalParameters: [setterParameter]))
- ], types: [
- _functionType
]))));
}
// 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(_callMethod(
+ block.add(ExpressionStatement(callMethodVarArgs(
getObjectProperty(),
- StringLiteral('defineProperty'),
+ 'defineProperty',
[
VariableGet(jsExporter),
- StringLiteral(exportName),
+ toJSString(exportName),
VariableGet(getSetMap)
],
VoidType()))
@@ -324,7 +360,8 @@
}
}
- block.add(ReturnStatement(VariableGet(jsExporter)));
+ 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(
@@ -335,24 +372,4 @@
..fileOffset = node.fileOffset
..parent = node.parent;
}
-
- // Optimize `callMethod` calls if possible.
- StaticInvocation _callMethod(Expression object, StringLiteral methodName,
- List<Expression> args, DartType returnType) {
- var index = args.length;
- var callMethodOptimized = _typeEnvironment.coreTypes.index
- .tryGetTopLevelMember(
- 'dart:js_util', '_callMethodUncheckedTrustType$index');
- if (callMethodOptimized == null) {
- var callMethod = _typeEnvironment.coreTypes.index
- .getTopLevelProcedure('dart:js_util', 'callMethod');
- return StaticInvocation(
- callMethod,
- Arguments([object, methodName, ListLiteral(args)],
- types: [returnType]));
- } else {
- return StaticInvocation(callMethodOptimized as Procedure,
- Arguments([object, methodName, ...args], types: [returnType]));
- }
- }
}
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index 7ab6395..71193d7 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -967,4 +967,15 @@
bool isNonLiteralConstructor(Procedure node) =>
_isStaticInteropConstructor(node, literal: false);
+
+ bool isStaticInteropType(DartType type) {
+ if (type is InterfaceType) {
+ return hasStaticInteropAnnotation(type.classNode);
+ } else if (type is ExtensionType) {
+ return isInteropInlineClass(type.extensionTypeDeclaration);
+ } else if (type is TypeParameterType) {
+ return isStaticInteropType(type.bound);
+ }
+ return false;
+ }
}
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
index ba28564..c0a11cd 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_validator.dart
@@ -12,8 +12,11 @@
show JsInteropDiagnosticReporter;
import 'package:_js_interop_checks/src/js_interop.dart' as js_interop;
import 'package:front_end/src/fasta/fasta_codes.dart'
- show templateJsInteropStaticInteropMockNotStaticInteropType;
+ show
+ templateJsInteropStaticInteropMockNotStaticInteropType,
+ templateJsInteropStaticInteropMockTypeParametersNotAllowed;
import 'package:kernel/ast.dart';
+import 'package:kernel/src/replacement_visitor.dart';
import 'package:kernel/type_environment.dart';
import 'export_checker.dart';
@@ -26,9 +29,10 @@
// members and those members' export names.
final Map<Class, Map<String, Set<ExtensionMemberDescriptor>>>
_staticInteropExportNameToDescriptorMap = {};
- final TypeEnvironment _typeEnvironment;
late final Map<Reference, Set<Extension>>
_staticInteropClassesWithExtensions = _computeStaticInteropExtensionMap();
+ final TypeEnvironment _typeEnvironment;
+ final TypeParameterResolver typeParameterResolver = TypeParameterResolver();
StaticInteropMockValidator(
this._diagnosticReporter, this._exportChecker, this._typeEnvironment);
@@ -43,6 +47,48 @@
node.name.text.length,
node.location?.file);
return false;
+ } else {
+ return _validateNoTypeParametersInTypeArgument(node, staticInteropType);
+ }
+ }
+
+ bool validateDartTypeArgument(StaticInvocation node, DartType dartType) =>
+ _validateNoTypeParametersInTypeArgument(node, dartType);
+
+ /// Validate that [type] argument does not pass type arguments beyond the
+ /// bounds.
+ ///
+ /// [node] is the createStaticInteropMock call that [type] occurs in.
+ ///
+ /// We do this check because reasoning about type arguments beyond their
+ /// bounds is complex and requires substitution in multiple places. It gets
+ /// even more complex when you have to account for extensions and supertypes
+ /// having their own type parameters too. In order to properly handle all
+ /// these cases, we'd have to keep constraints around and see what extensions
+ /// apply and what extensions don't. This may be simpler to do for inline
+ /// classes/extension types, as all the members are in the class and not in an
+ /// extension, but for now, we require that users must implement members with
+ /// type parameters based on their bounds.
+ ///
+ /// Returns whether the validation passed.
+ bool _validateNoTypeParametersInTypeArgument(
+ StaticInvocation node, DartType type) {
+ if (type is InterfaceType) {
+ final typeArguments = type.typeArguments;
+ final typeParams = type.classNode.typeParameters;
+ for (var i = 0; i < typeParams.length; i++) {
+ final arg = typeArguments[i];
+ // Uninstantiated type parameters are replaced with dynamic by the CFE.
+ if (arg is! DynamicType && arg != typeParams[i].bound) {
+ _diagnosticReporter.report(
+ templateJsInteropStaticInteropMockTypeParametersNotAllowed
+ .withArguments(type, true),
+ node.fileOffset,
+ node.name.text.length,
+ node.location?.file);
+ return false;
+ }
+ }
}
return true;
}
@@ -72,6 +118,7 @@
type = FunctionType([type], VoidType(), Nullability.nonNullable);
name += '=';
}
+ type = typeParameterResolver.resolve(type);
return '$extension.$name ($type)';
}).toList()
..sort();
@@ -161,15 +208,17 @@
return interopMember.function.positionalParameters[1].type;
} else {
assert(interopDescriptor.isMethod);
- var interopMemberType =
- interopMember.function.computeFunctionType(Nullability.nonNullable);
+ // We don't care about the method's own type parameters to determine
+ // subtyping. We simply substitute them by their bounds, if any.
+ final interopMemberType = interopMember.function
+ .computeThisFunctionType(Nullability.nonNullable)
+ .withoutTypeParameters;
// Ignore the first argument `this` in the generated procedure.
return FunctionType(
interopMemberType.positionalParameters.skip(1).toList(),
interopMemberType.returnType,
interopMemberType.declaredNullability,
namedParameters: interopMemberType.namedParameters,
- typeParameters: interopMemberType.typeParameters,
requiredParameterCount: interopMemberType.requiredParameterCount - 1);
}
}
@@ -190,8 +239,12 @@
}
bool isSubtypeOf(DartType dartType, DartType interopType) {
+ // Remove and substitute type parameters with their bounds/instantiated
+ // type arguments.
return _typeEnvironment.isSubtypeOf(
- dartType, interopType, SubtypeCheckMode.withNullabilities);
+ typeParameterResolver.resolve(dartType),
+ typeParameterResolver.resolve(interopType),
+ SubtypeCheckMode.withNullabilities);
}
var interopType = _getTypeOfDescriptor(interopDescriptor);
@@ -208,7 +261,8 @@
if (!isSubtypeOf(
(dartMember as Procedure)
.function
- .computeFunctionType(Nullability.nonNullable),
+ .computeThisFunctionType(Nullability.nonNullable)
+ .withoutTypeParameters,
interopType)) {
return false;
}
@@ -302,3 +356,18 @@
exportNameToDescriptors;
}
}
+
+/// Visitor that replaces each type parameter with its bound.
+///
+/// We use this to determine conformance of interop methods that use type
+/// parameters.
+class TypeParameterResolver extends ReplacementVisitor {
+ @override
+ DartType? visitTypeParameterType(TypeParameterType node, int variance) {
+ return node.resolveTypeParameterType;
+ }
+
+ DartType resolve(DartType node) {
+ return node.accept1(this, Variance.unrelated) ?? node;
+ }
+}
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 5b48751..e19af8c 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -109,9 +109,11 @@
'dart:_foreign_helper',
'dart:_interceptors',
'dart:_js_helper',
+ 'dart:_js_types',
'dart:_late_helper',
'dart:js',
'dart:js_interop',
+ 'dart:js_interop_unsafe',
'dart:js_util',
'dart:typed_data',
];
@@ -256,6 +258,8 @@
'dart:_interceptors',
'dart:_js_helper',
'dart:_late_helper',
+ // Needed since dart:js_util methods like createDartExport use this.
+ 'dart:js_interop_unsafe',
'dart:js_util'
];
@@ -294,8 +298,8 @@
'dart:isolate',
'dart:js',
'dart:js_interop',
- 'dart:js_util',
'dart:js_interop_unsafe',
+ 'dart:js_util',
'dart:math',
'dart:svg',
'dart:typed_data',
diff --git a/pkg/compiler/test/impact/data/jsinterop.dart b/pkg/compiler/test/impact/data/jsinterop.dart
index ddd2b24..7a0f3f3 100644
--- a/pkg/compiler/test/impact/data/jsinterop.dart
+++ b/pkg/compiler/test/impact/data/jsinterop.dart
@@ -32,6 +32,28 @@
/*member: JsInteropClass.method:type=[
native:GenericClass<dynamic>,
+ native:JSAny,
+ native:JSArray,
+ native:JSArrayBuffer,
+ native:JSBoolean,
+ native:JSBoxedDartObject,
+ native:JSDataView,
+ native:JSExportedDartFunction,
+ native:JSFloat32Array,
+ native:JSFloat64Array,
+ native:JSFunction,
+ native:JSInt16Array,
+ native:JSInt32Array,
+ native:JSInt8Array,
+ native:JSNumber,
+ native:JSObject,
+ native:JSPromise,
+ native:JSString,
+ native:JSTypedArray,
+ native:JSUint16Array,
+ native:JSUint32Array,
+ native:JSUint8Array,
+ native:JSUint8ClampedArray,
native:JsInteropClass]*/
@JS()
external double method();
diff --git a/pkg/compiler/test/impact/data/jsinterop_setter1.dart b/pkg/compiler/test/impact/data/jsinterop_setter1.dart
index eb917cb..8c61da5 100644
--- a/pkg/compiler/test/impact/data/jsinterop_setter1.dart
+++ b/pkg/compiler/test/impact/data/jsinterop_setter1.dart
@@ -55,6 +55,28 @@
native:DomError,
native:DomException,
native:ErrorEvent,
+ native:JSAny,
+ native:JSArray,
+ native:JSArrayBuffer,
+ native:JSBoolean,
+ native:JSBoxedDartObject,
+ native:JSDataView,
+ native:JSExportedDartFunction,
+ native:JSFloat32Array,
+ native:JSFloat64Array,
+ native:JSFunction,
+ native:JSInt16Array,
+ native:JSInt32Array,
+ native:JSInt8Array,
+ native:JSNumber,
+ native:JSObject,
+ native:JSPromise,
+ native:JSString,
+ native:JSTypedArray,
+ native:JSUint16Array,
+ native:JSUint32Array,
+ native:JSUint8Array,
+ native:JSUint8ClampedArray,
native:MediaError,
native:NavigatorUserMediaError,
native:OverconstrainedError,
diff --git a/pkg/compiler/test/impact/data/jsinterop_setter2.dart b/pkg/compiler/test/impact/data/jsinterop_setter2.dart
index 7f5b60b..742bc0f 100644
--- a/pkg/compiler/test/impact/data/jsinterop_setter2.dart
+++ b/pkg/compiler/test/impact/data/jsinterop_setter2.dart
@@ -62,6 +62,28 @@
native:DomException,
native:ErrorEvent,
native:File,
+ native:JSAny,
+ native:JSArray,
+ native:JSArrayBuffer,
+ native:JSBoolean,
+ native:JSBoxedDartObject,
+ native:JSDataView,
+ native:JSExportedDartFunction,
+ native:JSFloat32Array,
+ native:JSFloat64Array,
+ native:JSFunction,
+ native:JSInt16Array,
+ native:JSInt32Array,
+ native:JSInt8Array,
+ native:JSNumber,
+ native:JSObject,
+ native:JSPromise,
+ native:JSString,
+ native:JSTypedArray,
+ native:JSUint16Array,
+ native:JSUint32Array,
+ native:JSUint8Array,
+ native:JSUint8ClampedArray,
native:MediaError,
native:NavigatorUserMediaError,
native:OverconstrainedError,
diff --git a/pkg/dart2wasm/lib/js/callback_specializer.dart b/pkg/dart2wasm/lib/js/callback_specializer.dart
index 448c5f6..3f39cd4 100644
--- a/pkg/dart2wasm/lib/js/callback_specializer.dart
+++ b/pkg/dart2wasm/lib/js/callback_specializer.dart
@@ -1,7 +1,8 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-
+import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
+ show InlineExtensionIndex;
import 'package:dart2wasm/js/method_collector.dart';
import 'package:dart2wasm/js/util.dart';
import 'package:kernel/ast.dart';
@@ -12,9 +13,10 @@
final StatefulStaticTypeContext _staticTypeContext;
final MethodCollector _methodCollector;
final CoreTypesUtil _util;
+ final InlineExtensionIndex _inlineExtensionIndex;
- CallbackSpecializer(
- this._staticTypeContext, this._util, this._methodCollector) {}
+ CallbackSpecializer(this._staticTypeContext, this._util,
+ this._methodCollector, this._inlineExtensionIndex) {}
bool _needsArgumentsLength(FunctionType type) =>
type.requiredParameterCount < type.positionalParameters.length;
@@ -30,7 +32,8 @@
DartType callbackParameterType = function.positionalParameters[i];
Expression expression;
VariableGet v = VariableGet(positionalParameters[i]);
- if (callbackParameterType.isStaticInteropType && boxExternRef) {
+ if (_inlineExtensionIndex.isStaticInteropType(callbackParameterType) &&
+ boxExternRef) {
expression = _createJSValue(v);
} else {
expression = AsExpression(
diff --git a/pkg/dart2wasm/lib/js/interop_specializer.dart b/pkg/dart2wasm/lib/js/interop_specializer.dart
index 889aeeb..bb1acc1 100644
--- a/pkg/dart2wasm/lib/js/interop_specializer.dart
+++ b/pkg/dart2wasm/lib/js/interop_specializer.dart
@@ -330,7 +330,8 @@
_util.jsifyTarget(expr.getStaticType(_staticTypeContext)),
Arguments([expr])))
.toList();
- assert(function.returnType.isStaticInteropType);
+ assert(
+ factory._inlineExtensionIndex.isStaticInteropType(function.returnType));
return invokeOneArg(_util.jsValueBoxTarget,
StaticInvocation(interopProcedure, Arguments(positionalArgs)));
}
@@ -345,12 +346,8 @@
late String _libraryJSString;
late final InlineExtensionIndex _inlineExtensionIndex;
- InteropSpecializerFactory(
- this._staticTypeContext, this._util, this._methodCollector) {
- final typeEnvironment = _staticTypeContext.typeEnvironment;
- _inlineExtensionIndex =
- InlineExtensionIndex(typeEnvironment.coreTypes, typeEnvironment);
- }
+ InteropSpecializerFactory(this._staticTypeContext, this._util,
+ this._methodCollector, this._inlineExtensionIndex);
void enterLibrary(Library library) {
_libraryJSString = getJSName(library);
diff --git a/pkg/dart2wasm/lib/js/interop_transformer.dart b/pkg/dart2wasm/lib/js/interop_transformer.dart
index a0f8c90..187144c 100644
--- a/pkg/dart2wasm/lib/js/interop_transformer.dart
+++ b/pkg/dart2wasm/lib/js/interop_transformer.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
+ show InlineExtensionIndex;
import 'package:dart2wasm/js/callback_specializer.dart';
import 'package:dart2wasm/js/inline_expander.dart';
import 'package:dart2wasm/js/interop_specializer.dart';
@@ -31,22 +33,28 @@
final MethodCollector _methodCollector;
final CoreTypesUtil _util;
- InteropTransformer._(
- this._staticTypeContext, this._util, this._methodCollector)
- : _callbackSpecializer =
- CallbackSpecializer(_staticTypeContext, _util, _methodCollector),
+ InteropTransformer._(this._staticTypeContext, this._util,
+ this._methodCollector, inlineExtensionIndex)
+ : _callbackSpecializer = CallbackSpecializer(
+ _staticTypeContext, _util, _methodCollector, inlineExtensionIndex),
_inlineExpander =
InlineExpander(_staticTypeContext, _util, _methodCollector),
_interopSpecializerFactory = InteropSpecializerFactory(
- _staticTypeContext, _util, _methodCollector) {}
+ _staticTypeContext,
+ _util,
+ _methodCollector,
+ inlineExtensionIndex) {}
factory InteropTransformer(CoreTypes coreTypes, ClassHierarchy hierarchy) {
- final util = CoreTypesUtil(coreTypes);
+ final typeEnvironment = TypeEnvironment(coreTypes, hierarchy);
+ final inlineExtensionIndex =
+ InlineExtensionIndex(coreTypes, typeEnvironment);
+ final util = CoreTypesUtil(coreTypes, inlineExtensionIndex);
return InteropTransformer._(
- StatefulStaticTypeContext.stacked(
- TypeEnvironment(coreTypes, hierarchy)),
+ StatefulStaticTypeContext.stacked(typeEnvironment),
util,
- MethodCollector(util));
+ MethodCollector(util),
+ inlineExtensionIndex);
}
@override
diff --git a/pkg/dart2wasm/lib/js/util.dart b/pkg/dart2wasm/lib/js/util.dart
index 1c3f1a9..00f83e3 100644
--- a/pkg/dart2wasm/lib/js/util.dart
+++ b/pkg/dart2wasm/lib/js/util.dart
@@ -2,8 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:_js_interop_checks/src/js_interop.dart'
- show hasJSInteropAnnotation, hasStaticInteropAnnotation;
+import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
+ show InlineExtensionIndex;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
@@ -11,6 +11,7 @@
/// A utility wrapper for [CoreTypes].
class CoreTypesUtil {
+ final InlineExtensionIndex _inlineExtensionIndex;
final CoreTypes coreTypes;
final Procedure allowInteropTarget;
final Procedure dartifyRawTarget;
@@ -26,7 +27,7 @@
final Class wasmExternRefClass;
final Procedure wrapDartFunctionTarget;
- CoreTypesUtil(this.coreTypes)
+ CoreTypesUtil(this.coreTypes, this._inlineExtensionIndex)
: allowInteropTarget = coreTypes.index
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
dartifyRawTarget = coreTypes.index
@@ -72,7 +73,9 @@
wasmExternRefClass.getThisType(coreTypes, Nullability.nullable);
Procedure jsifyTarget(DartType type) =>
- type.isStaticInteropType ? jsValueUnboxTarget : jsifyRawTarget;
+ _inlineExtensionIndex.isStaticInteropType(type)
+ ? jsValueUnboxTarget
+ : jsifyRawTarget;
void annotateProcedure(
Procedure procedure, String pragmaOptionString, AnnotationType type) {
@@ -109,7 +112,7 @@
return invokeOneArg(dartifyRawTarget, invocation);
} else {
Expression expression;
- if (returnType.isStaticInteropType) {
+ if (_inlineExtensionIndex.isStaticInteropType(returnType)) {
// TODO(joshualitt): Expose boxed `JSNull` and `JSUndefined` to Dart
// code after migrating existing users of js interop on Dart2Wasm.
// expression = _createJSValue(invocation);
@@ -179,16 +182,6 @@
}
}
-extension DartTypeExtension on DartType {
- bool get isStaticInteropType {
- final type = this;
- return (type is InterfaceType &&
- hasStaticInteropAnnotation(type.classReference.asClass)) ||
- (type is ExtensionType &&
- hasJSInteropAnnotation(type.extensionTypeDeclaration));
- }
-}
-
StaticInvocation invokeOneArg(Procedure target, Expression arg) =>
StaticInvocation(target, Arguments([arg]));
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index dbc2972..16d77c7 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -120,6 +120,7 @@
'dart:_wasm',
'dart:collection',
'dart:js_interop',
+ 'dart:js_interop_unsafe',
'dart:js_util',
'dart:typed_data',
];
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index c2ae514..fad5748 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -101,6 +101,7 @@
'dart:js',
'dart:js_util',
'dart:js_interop',
+ 'dart:js_interop_unsafe',
'dart:math',
'dart:svg',
'dart:typed_data',
@@ -109,6 +110,7 @@
'dart:_foreign_helper',
'dart:_interceptors',
'dart:_js_helper',
+ 'dart:_js_types',
'dart:_native_typed_data',
'dart:_runtime',
'dart:_rti',
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index 2b7cf77..3c7bdf5 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -3939,6 +3939,38 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
+ templateJsInteropStaticInteropMockTypeParametersNotAllowed = const Template<
+ Message Function(DartType _type, bool isNonNullableByDefault)>(
+ problemMessageTemplate:
+ r"""Type argument '#type' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.""",
+ correctionMessageTemplate:
+ r"""Remove the type parameter in the type argument or replace it with its bound.""",
+ withArguments:
+ _withArgumentsJsInteropStaticInteropMockTypeParametersNotAllowed);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(DartType _type, bool isNonNullableByDefault)>
+ codeJsInteropStaticInteropMockTypeParametersNotAllowed =
+ const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
+ "JsInteropStaticInteropMockTypeParametersNotAllowed",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsJsInteropStaticInteropMockTypeParametersNotAllowed(
+ DartType _type, bool isNonNullableByDefault) {
+ TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
+ List<Object> typeParts = labeler.labelType(_type);
+ String type = typeParts.join();
+ return new Message(codeJsInteropStaticInteropMockTypeParametersNotAllowed,
+ problemMessage:
+ """Type argument '${type}' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.""" +
+ labeler.originMessages,
+ correctionMessage: """Remove the type parameter in the type argument or replace it with its bound.""",
+ arguments: {'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateJsInteropStrictModeViolation = const Template<
Message Function(DartType _type, bool isNonNullableByDefault)>(
problemMessageTemplate:
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 1a5347b..735b835 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -629,6 +629,8 @@
JsInteropStaticInteropMockMissingImplements/example: Fail # Web compiler specific
JsInteropStaticInteropMockNotStaticInteropType/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropMockNotStaticInteropType/example: Fail # Web compiler specific
+JsInteropStaticInteropMockTypeParametersNotAllowed/analyzerCode: Fail # Web compiler specific
+JsInteropStaticInteropMockTypeParametersNotAllowed/example: Fail # Web compiler specific
JsInteropStaticInteropNoJSAnnotation/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropNoJSAnnotation/example: Fail # Web compiler specific
JsInteropStaticInteropParameterInitializersAreIgnored/analyzerCode: Fail # Web compiler specific
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index c171b07..d75e3ea 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5495,7 +5495,7 @@
severity: WARNING
JsInteropExportDisallowedMember:
- problemMessage: "Member '#name' is not a concrete instance member, and therefore can't be exported."
+ problemMessage: "Member '#name' is not a concrete instance member or declares type parameters, and therefore can't be exported."
correctionMessage: "Remove the `@JSExport` annotation from the member, and use an instance member to call this member instead."
JsInteropExportInvalidInteropTypeArgument:
@@ -5597,6 +5597,10 @@
problemMessage: "Type argument '#type' needs to be a `@staticInterop` type."
correctionMessage: "Use a `@staticInterop` class instead."
+JsInteropStaticInteropMockTypeParametersNotAllowed:
+ problemMessage: "Type argument '#type' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance."
+ correctionMessage: "Remove the type parameter in the type argument or replace it with its bound."
+
JsInteropStaticInteropNoJSAnnotation:
problemMessage: "`@staticInterop` classes should also have the `@JS` annotation."
correctionMessage: "Add `@JS` to class '#name'."
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index a2689be..7ca72f8 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -28,9 +28,11 @@
collide
compilercontext.runincontext
compilesdk
+conformance
constructor(s)
core
count.#count
+createstaticinteropmock
d
dart.dev
dart2js_server
@@ -67,6 +69,7 @@
macro
member(s)
migrate
+mocking
n
name.#name
name.stack
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart b/pkg/front_end/testcases/dart2js/inline_class/external.dart
index c9d2ff0..fb5fa0d 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart
@@ -23,9 +23,7 @@
external A method();
- // TODO: Once https://github.com/dart-lang/sdk/issues/53046 is resolved,
- // uncomment this, the static variant below, and the usage of the members.
- // external T genericMethod<T extends B>(T t);
+ external T genericMethod<T extends B>(T t);
external B get getter;
@@ -35,7 +33,7 @@
external static A staticMethod();
- // external static T staticGenericMethod<T extends B>(T t);
+ external static T staticGenericMethod<T extends B>(T t);
external static B get staticGetter;
@@ -48,13 +46,13 @@
a = b1.field;
b1.field = a;
a = b1.method();
- // b2 = b2.genericMethod(b2);
+ b2 = b2.genericMethod(b2);
b1 = b2.getter;
b1.setter = b2;
a = B.staticField;
B.staticField = a;
a = B.staticMethod();
- // b2 = B.staticGenericMethod(b2);
+ b2 = B.staticGenericMethod(b2);
b1 = B.staticGetter;
B.staticSetter = b2;
}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.transformed.expect
index 7d84e0f..292c0f8 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.strong.transformed.expect
@@ -23,10 +23,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -46,11 +49,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => js_2::_callMethodUnchecked0<self::A>(#this, "method");
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => js_2::callMethod<T>(#this, "genericMethod", <dynamic>[t]);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -59,11 +66,13 @@
a = js_2::getProperty<self::A>(b1, "field");
js_2::setProperty<self::A>(b1, "field", a);
a = js_2::_callMethodUnchecked0<self::A>(b1, "method");
+ b2 = js_2::callMethod<self::B>(b2, "genericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(b2, "getter");
js_2::setProperty<self::B>(b1, "setter", b2);
a = js_2::getProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField");
js_2::setProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField", a);
a = js_2::_callMethodUnchecked0<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticMethod");
+ b2 = js_2::callMethod<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGenericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGetter");
js_2::setProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticSetter", b2);
}
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.textual_outline.expect
index 8fa81c1..9c5e795c 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.textual_outline.expect
@@ -11,10 +11,12 @@
external B.named(int i);
external A field;
external A method();
+ external T genericMethod<T extends B>(T t);
external B get getter;
external void set setter(B b);
external static A staticField;
external static A staticMethod();
+ external static T staticGenericMethod<T extends B>(T t);
external static B get staticGetter;
external static void set staticSetter(B b);
}
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.modular.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.modular.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.modular.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.outline.expect
index 17dba4e..660817e 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.outline.expect
@@ -21,10 +21,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -44,11 +47,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void
@@ -60,4 +67,4 @@
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:10:2 -> InstanceConstant(const JS{JS.name: null})
Evaluated: StaticGet @ org-dartlang-testcase:///external.dart:11:2 -> InstanceConstant(const _StaticInterop{})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:14:2 -> InstanceConstant(const JS{JS.name: null})
-Extra constant evaluation: evaluated: 12, effectively constant: 4
+Extra constant evaluation: evaluated: 16, effectively constant: 4
diff --git a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.transformed.expect
index 7d84e0f..292c0f8 100644
--- a/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/inline_class/external.dart.weak.transformed.expect
@@ -23,10 +23,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -46,11 +49,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => js_2::_callMethodUnchecked0<self::A>(#this, "method");
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => js_2::callMethod<T>(#this, "genericMethod", <dynamic>[t]);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -59,11 +66,13 @@
a = js_2::getProperty<self::A>(b1, "field");
js_2::setProperty<self::A>(b1, "field", a);
a = js_2::_callMethodUnchecked0<self::A>(b1, "method");
+ b2 = js_2::callMethod<self::B>(b2, "genericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(b2, "getter");
js_2::setProperty<self::B>(b1, "setter", b2);
a = js_2::getProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField");
js_2::setProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField", a);
a = js_2::_callMethodUnchecked0<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticMethod");
+ b2 = js_2::callMethod<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGenericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGetter");
js_2::setProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticSetter", b2);
}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart b/pkg/front_end/testcases/dartdevc/inline_class/external.dart
index f9d1c9b..c7fbdae 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart
@@ -24,9 +24,7 @@
external A method();
- // TODO: Once https://github.com/dart-lang/sdk/issues/53046 is resolved,
- // uncomment this, the static variant below, and the usage of the members.
- // external T genericMethod<T extends B>(T t);
+ external T genericMethod<T extends B>(T t);
external B get getter;
@@ -36,7 +34,7 @@
external static A staticMethod();
- // external static T staticGenericMethod<T extends B>(T t);
+ external static T staticGenericMethod<T extends B>(T t);
external static B get staticGetter;
@@ -49,13 +47,13 @@
a = b1.field;
b1.field = a;
a = b1.method();
- // b2 = b2.genericMethod(b2);
+ b2 = b2.genericMethod(b2);
b1 = b2.getter;
b1.setter = b2;
a = B.staticField;
B.staticField = a;
a = B.staticMethod();
- // b2 = B.staticGenericMethod(b2);
+ b2 = B.staticGenericMethod(b2);
b1 = B.staticGetter;
B.staticSetter = b2;
}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.transformed.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.transformed.expect
index 7d84e0f..292c0f8 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.strong.transformed.expect
@@ -23,10 +23,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -46,11 +49,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => js_2::_callMethodUnchecked0<self::A>(#this, "method");
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => js_2::callMethod<T>(#this, "genericMethod", <dynamic>[t]);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -59,11 +66,13 @@
a = js_2::getProperty<self::A>(b1, "field");
js_2::setProperty<self::A>(b1, "field", a);
a = js_2::_callMethodUnchecked0<self::A>(b1, "method");
+ b2 = js_2::callMethod<self::B>(b2, "genericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(b2, "getter");
js_2::setProperty<self::B>(b1, "setter", b2);
a = js_2::getProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField");
js_2::setProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField", a);
a = js_2::_callMethodUnchecked0<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticMethod");
+ b2 = js_2::callMethod<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGenericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGetter");
js_2::setProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticSetter", b2);
}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.textual_outline.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.textual_outline.expect
index 36defdf..9c5e795c 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.textual_outline.expect
@@ -1,12 +1,9 @@
@JS()
library static_interop;
-
import 'dart:js_interop';
-
@JS()
@staticInterop
class A {}
-
@JS()
inline class B {
final A a;
@@ -14,12 +11,13 @@
external B.named(int i);
external A field;
external A method();
+ external T genericMethod<T extends B>(T t);
external B get getter;
external void set setter(B b);
external static A staticField;
external static A staticMethod();
+ external static T staticGenericMethod<T extends B>(T t);
external static B get staticGetter;
external static void set staticSetter(B b);
}
-
void method(A a) {}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.modular.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.modular.expect
index fe48805..fc20f1d 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.modular.expect
@@ -22,10 +22,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -45,11 +48,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -58,11 +65,13 @@
a = self::B|get#field(b1);
self::B|set#field(b1, a);
a = self::B|method(b1);
+ b2 = self::B|genericMethod<self::B>(b2, b2);
b1 = self::B|get#getter(b2);
self::B|set#setter(b1, b2);
a = self::B|staticField;
self::B|staticField = a;
a = self::B|staticMethod();
+ b2 = self::B|staticGenericMethod<self::B>(b2);
b1 = self::B|staticGetter;
self::B|staticSetter = b2;
}
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.outline.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.outline.expect
index 12c4e1a..53adbd8 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.outline.expect
@@ -21,10 +21,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -44,11 +47,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => self::B|method(#this);
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => self::B|genericMethod<T>(#this, t);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void
@@ -60,4 +67,4 @@
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:11:2 -> InstanceConstant(const JS{JS.name: null})
Evaluated: StaticGet @ org-dartlang-testcase:///external.dart:12:2 -> InstanceConstant(const _StaticInterop{})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///external.dart:15:2 -> InstanceConstant(const JS{JS.name: null})
-Extra constant evaluation: evaluated: 12, effectively constant: 4
+Extra constant evaluation: evaluated: 16, effectively constant: 4
diff --git a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.transformed.expect b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.transformed.expect
index 7d84e0f..292c0f8 100644
--- a/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dartdevc/inline_class/external.dart.weak.transformed.expect
@@ -23,10 +23,13 @@
set field = self::B|set#field;
method method = self::B|method;
tearoff method = self::B|get#method;
+ method genericMethod = self::B|genericMethod;
+ tearoff genericMethod = self::B|get#genericMethod;
get getter = self::B|get#getter;
static get staticField = get self::B|staticField;
static set staticField = set self::B|staticField;
static method staticMethod = self::B|staticMethod;
+ static method staticGenericMethod = self::B|staticGenericMethod;
static get staticGetter = get self::B|staticGetter;
set setter = self::B|set#setter;
static set staticSetter = set self::B|staticSetter;
@@ -46,11 +49,15 @@
external static inline-class-member method B|method(lowered final self::B #this) → self::A;
static inline-class-member method B|get#method(lowered final self::B #this) → () → self::A
return () → self::A => js_2::_callMethodUnchecked0<self::A>(#this, "method");
+external static inline-class-member method B|genericMethod<T extends self::B>(lowered final self::B #this, self::B|genericMethod::T t) → self::B|genericMethod::T;
+static inline-class-member method B|get#genericMethod(lowered final self::B #this) → <T extends self::B>(T) → T
+ return <T extends self::B>(T t) → T => js_2::callMethod<T>(#this, "genericMethod", <dynamic>[t]);
external static inline-class-member method B|get#getter(lowered final self::B #this) → self::B;
external static inline-class-member method B|set#setter(lowered final self::B #this, self::B b) → void;
external static inline-class-member get B|staticField() → self::A;
external static inline-class-member set B|staticField(self::A #externalFieldValue) → void;
external static inline-class-member method B|staticMethod() → self::A;
+external static inline-class-member method B|staticGenericMethod<T extends self::B>(self::B|staticGenericMethod::T t) → self::B|staticGenericMethod::T;
external static inline-class-member get B|staticGetter() → self::B;
external static inline-class-member set B|staticSetter(self::B b) → void;
static method method(self::A a) → void {
@@ -59,11 +66,13 @@
a = js_2::getProperty<self::A>(b1, "field");
js_2::setProperty<self::A>(b1, "field", a);
a = js_2::_callMethodUnchecked0<self::A>(b1, "method");
+ b2 = js_2::callMethod<self::B>(b2, "genericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(b2, "getter");
js_2::setProperty<self::B>(b1, "setter", b2);
a = js_2::getProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField");
js_2::setProperty<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticField", a);
a = js_2::_callMethodUnchecked0<self::A>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticMethod");
+ b2 = js_2::callMethod<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGenericMethod", <dynamic>[b2]);
b1 = js_2::getProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticGetter");
js_2::setProperty<self::B>(js_2::_getPropertyTrustType<core::Object>(js_2::globalThis, "B"), "staticSetter", b2);
}
diff --git a/tests/lib/js/export/static_interop_mock/functional_test_lib.dart b/tests/lib/js/export/static_interop_mock/functional_test_lib.dart
index ba50b89..2994caf 100644
--- a/tests/lib/js/export/static_interop_mock/functional_test_lib.dart
+++ b/tests/lib/js/export/static_interop_mock/functional_test_lib.dart
@@ -6,8 +6,12 @@
// (final and not), getters, and setters are tested along with potential
// renames.
+@JS()
+library functional_test_lib;
+
+import 'dart:js_interop';
+
import 'package:expect/minitest.dart';
-import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS()
@@ -21,7 +25,7 @@
@JS('_rename')
external String rename();
external String optionalConcat(String foo, String bar,
- [String boo = '', String? baz]);
+ [String boo, String? baz]);
}
@JSExport()
@@ -84,6 +88,58 @@
String _differentNameSameRename = 'initialized';
}
+@JS()
+@staticInterop
+class Supersupertype {}
+
+@JS()
+@staticInterop
+class Supertype<T extends JSObject> implements Supersupertype {}
+
+extension SupertypeExtension<T extends JSObject> on Supertype<T> {
+ external T superMethod(T param);
+}
+
+// Note that even though `Supertype` is instantiated with `JSArray`, we still
+// require users to implement `superMethod` using the `JSObject` bound.
+// Functionally, this means `superMethod` needs to accept a `JSObject` (or a
+// supertype), even though an object typed `Params` can never actually call
+// `superMethod` with a type less specific than `JSArray`. This is probably
+// rare, but can be a bit frustrating. A similar analysis can be made for the
+// type parameters on `Params`. A user may want to mock only a
+// `Params<JSArray, Params>`, but we require them to mock a
+// `Params<JSObject, Supertype>` as those are the bounds. Unlike the
+// `superMethod` case however, we have an error check when trying to pass in
+// such a type to `createStaticInteropMock` to reduce confusion.
+@JS()
+@staticInterop
+class Params<T extends JSObject, U extends Supertype>
+ extends Supertype<JSArray> {
+ external factory Params();
+}
+
+extension ParamsExtension<T extends JSObject, U extends Supertype>
+ on Params<T, U> {
+ external T get getSet;
+ external set getSet(T val);
+ external T method(T param);
+ external U interopTypeMethod(U param);
+ external V genericMethod<V extends JSObject>(V param);
+}
+
+late JSObject _jsObject;
+
+@JSExport()
+class ParamsImpl<S extends JSObject, T extends JSObject, U extends Supertype> {
+ S superMethod(S param) => param;
+
+ T get getSet => _jsObject as T;
+ set getSet(T val) => _jsObject = val;
+ T method(T param) => param;
+ U interopTypeMethod(U param) => param;
+ JSObject genericMethod(JSObject param) => param;
+}
+
void test([Object? proto]) {
var jsMethods =
createStaticInteropMock<Methods, MethodsDart>(MethodsDart(), proto);
@@ -138,4 +194,18 @@
expect(jsGetSet.renamedGetSet, 'dartModified');
expect(jsGetSet.sameNameDifferentRename, 'dartModifiedGet');
expect(jsGetSet.differentNameSameRenameGet, 'dartModified');
+ // Calling members with type parameters.
+ final jsParams = createStaticInteropMock<Params, ParamsImpl>(ParamsImpl());
+ final jsArray = JSArray();
+ expect(jsParams.superMethod(jsArray), jsArray);
+ _jsObject = newObject<JSObject>();
+ expect(jsParams.getSet, _jsObject);
+ final newJsObject = newObject<JSObject>();
+ jsParams.getSet = newJsObject;
+ expect(jsParams.getSet, newJsObject);
+ expect(jsParams.method(_jsObject), _jsObject);
+ expect(jsParams.interopTypeMethod(_jsObject as Supertype), _jsObject);
+ expect(jsParams.genericMethod(_jsObject), _jsObject);
+ expect(jsParams.genericMethod<JSObject>(_jsObject), _jsObject);
+ expect(jsParams.genericMethod<JSArray>(jsArray), jsArray);
}
diff --git a/tests/lib/js/export/static_interop_mock/type_parameter_static_test.dart b/tests/lib/js/export/static_interop_mock/type_parameter_static_test.dart
new file mode 100644
index 0000000..9d933ce
--- /dev/null
+++ b/tests/lib/js/export/static_interop_mock/type_parameter_static_test.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that `createStaticInteropMock` correctly instantiates to bounds for type
+// parameters.
+
+import 'dart:js_interop';
+import 'package:js/js_util.dart';
+
+import 'functional_test_lib.dart';
+
+@JSExport()
+class Valid {
+ JSArray superMethod(JSAny param) => throw UnimplementedError();
+
+ JSArray get getSet => throw UnimplementedError();
+ set getSet(JSAny val) => throw UnimplementedError();
+ JSArray method(JSAny param) => throw UnimplementedError();
+ Params interopTypeMethod(Supertype param) => throw UnimplementedError();
+ JSArray genericMethod(JSAny param) => throw UnimplementedError();
+}
+
+@JSExport()
+class Invalid {
+ JSAny superMethod(JSArray param) => throw UnimplementedError();
+
+ @JSExport('getSet') // Rename to avoid getter/setter type conflicts.
+ JSAny get getter => throw UnimplementedError();
+ set getSet(JSArray val) => throw UnimplementedError();
+ JSAny method(JSArray param) => throw UnimplementedError();
+ Supersupertype interopTypeMethod(Params param) => throw UnimplementedError();
+ JSAny genericMethod(JSArray param) => throw UnimplementedError();
+}
+
+@JSExport()
+class InvalidContravariant {
+ JSArray superMethod(JSArray param) => throw UnimplementedError();
+
+ JSArray get getSet => throw UnimplementedError();
+ set getSet(JSArray val) => throw UnimplementedError();
+ JSArray method(JSArray param) => throw UnimplementedError();
+ Params interopTypeMethod(Params param) => throw UnimplementedError();
+ JSArray genericMethod(JSArray param) => throw UnimplementedError();
+}
+
+@JSExport()
+class InvalidCovariant {
+ JSAny superMethod(JSAny param) => throw UnimplementedError();
+
+ JSAny get getSet => throw UnimplementedError();
+ set getSet(JSAny val) => throw UnimplementedError();
+ JSAny method(JSAny param) => throw UnimplementedError();
+ Supersupertype interopTypeMethod(Supersupertype param) =>
+ throw UnimplementedError();
+ JSAny genericMethod(JSAny param) => throw UnimplementedError();
+}
+
+void main() {
+ createStaticInteropMock<
+//^
+// [web] Type argument 'Params<JSArray, Params<JSObject, Supertype<JSObject>>>' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.
+// [web] Type argument 'ParamsImpl<JSArray, JSArray, Params<JSObject, Supertype<JSObject>>>' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.
+ Params<JSArray, Params>,
+ ParamsImpl<JSArray, JSArray, Params>>(
+ ParamsImpl<JSArray, JSArray, Params>());
+ createStaticInteropMock<Params<JSObject, Supertype>,
+ ParamsImpl<JSObject, JSObject, Supertype>>(
+ ParamsImpl<JSObject, JSObject, Supertype>());
+
+ // Note that this is fine, but might fail at runtime due to runtime covariant
+ // checks. This is no different than casting a `List<JSObject>` to `List` and
+ // trying to add a `JSString`. On the JS backends, this will fail, and on
+ // dart2wasm, this will succeed because all JS types get erased to JSValue.
+ createStaticInteropMock<Params, ParamsImpl>(
+ ParamsImpl<JSArray, JSArray, Supertype>());
+ createStaticInteropMock<Params, ParamsImpl>(ParamsImpl());
+ createStaticInteropMock<Params, Valid>(Valid());
+
+ createStaticInteropMock<Params, Invalid>(Invalid());
+//^
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet (FunctionType(JSObject Function())), ParamsExtension.getSet= (FunctionType(void Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+ createStaticInteropMock<Params, InvalidContravariant>(InvalidContravariant());
+//^
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' has a getter, but does not have a setter to implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet= (FunctionType(void Function(JSObject))).
+ createStaticInteropMock<Params, InvalidCovariant>(InvalidCovariant());
+//^
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' has a setter, but does not have a getter to implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet (FunctionType(JSObject Function())).
+}
diff --git a/tests/lib/js/export/validation_test.dart b/tests/lib/js/export/validation_test.dart
index af2fab8..5577778 100644
--- a/tests/lib/js/export/validation_test.dart
+++ b/tests/lib/js/export/validation_test.dart
@@ -179,9 +179,37 @@
createDartExport(ClassWithValue());
}
+// `JSExport` classes can't export methods that define type parameters as those
+// type parameters will never be instantiated through interop. Class type
+// parameters are okay, however.
+@JSExport()
+class GenericAll {
+// ^
+// [web] Class 'GenericAll' has no exportable members in the class or the inheritance chain.
+ void defineTypeParam<T extends int>() {}
+ T useTypeParam<T extends Object>(T t) => t;
+}
+
+class GenericSome<U> {
+ @JSExport()
+ void defineTypeParam<T extends int>() {}
+ // ^
+ // [web] Member 'defineTypeParam' is not a concrete instance member or declares type parameters, and therefore can't be exported.
+ T useTypeParam<T extends Object>(T t) => t;
+ @JSExport()
+ U useClassParam(U u) => u;
+}
+
+void testClassWithGenerics() {
+ createDartExport(GenericAll());
+ createDartExport(GenericSome());
+ createDartExport(GenericSome<int>());
+}
+
void main() {
testNumberOfExports();
testUseDartInterface();
testCollisions();
testClassExportWithValue();
+ testClassWithGenerics();
}
diff --git a/tests/lib_2/js/export/static_interop_mock/functional_test_lib.dart b/tests/lib_2/js/export/static_interop_mock/functional_test_lib.dart
index ba50b89..2994caf 100644
--- a/tests/lib_2/js/export/static_interop_mock/functional_test_lib.dart
+++ b/tests/lib_2/js/export/static_interop_mock/functional_test_lib.dart
@@ -6,8 +6,12 @@
// (final and not), getters, and setters are tested along with potential
// renames.
+@JS()
+library functional_test_lib;
+
+import 'dart:js_interop';
+
import 'package:expect/minitest.dart';
-import 'package:js/js.dart';
import 'package:js/js_util.dart';
@JS()
@@ -21,7 +25,7 @@
@JS('_rename')
external String rename();
external String optionalConcat(String foo, String bar,
- [String boo = '', String? baz]);
+ [String boo, String? baz]);
}
@JSExport()
@@ -84,6 +88,58 @@
String _differentNameSameRename = 'initialized';
}
+@JS()
+@staticInterop
+class Supersupertype {}
+
+@JS()
+@staticInterop
+class Supertype<T extends JSObject> implements Supersupertype {}
+
+extension SupertypeExtension<T extends JSObject> on Supertype<T> {
+ external T superMethod(T param);
+}
+
+// Note that even though `Supertype` is instantiated with `JSArray`, we still
+// require users to implement `superMethod` using the `JSObject` bound.
+// Functionally, this means `superMethod` needs to accept a `JSObject` (or a
+// supertype), even though an object typed `Params` can never actually call
+// `superMethod` with a type less specific than `JSArray`. This is probably
+// rare, but can be a bit frustrating. A similar analysis can be made for the
+// type parameters on `Params`. A user may want to mock only a
+// `Params<JSArray, Params>`, but we require them to mock a
+// `Params<JSObject, Supertype>` as those are the bounds. Unlike the
+// `superMethod` case however, we have an error check when trying to pass in
+// such a type to `createStaticInteropMock` to reduce confusion.
+@JS()
+@staticInterop
+class Params<T extends JSObject, U extends Supertype>
+ extends Supertype<JSArray> {
+ external factory Params();
+}
+
+extension ParamsExtension<T extends JSObject, U extends Supertype>
+ on Params<T, U> {
+ external T get getSet;
+ external set getSet(T val);
+ external T method(T param);
+ external U interopTypeMethod(U param);
+ external V genericMethod<V extends JSObject>(V param);
+}
+
+late JSObject _jsObject;
+
+@JSExport()
+class ParamsImpl<S extends JSObject, T extends JSObject, U extends Supertype> {
+ S superMethod(S param) => param;
+
+ T get getSet => _jsObject as T;
+ set getSet(T val) => _jsObject = val;
+ T method(T param) => param;
+ U interopTypeMethod(U param) => param;
+ JSObject genericMethod(JSObject param) => param;
+}
+
void test([Object? proto]) {
var jsMethods =
createStaticInteropMock<Methods, MethodsDart>(MethodsDart(), proto);
@@ -138,4 +194,18 @@
expect(jsGetSet.renamedGetSet, 'dartModified');
expect(jsGetSet.sameNameDifferentRename, 'dartModifiedGet');
expect(jsGetSet.differentNameSameRenameGet, 'dartModified');
+ // Calling members with type parameters.
+ final jsParams = createStaticInteropMock<Params, ParamsImpl>(ParamsImpl());
+ final jsArray = JSArray();
+ expect(jsParams.superMethod(jsArray), jsArray);
+ _jsObject = newObject<JSObject>();
+ expect(jsParams.getSet, _jsObject);
+ final newJsObject = newObject<JSObject>();
+ jsParams.getSet = newJsObject;
+ expect(jsParams.getSet, newJsObject);
+ expect(jsParams.method(_jsObject), _jsObject);
+ expect(jsParams.interopTypeMethod(_jsObject as Supertype), _jsObject);
+ expect(jsParams.genericMethod(_jsObject), _jsObject);
+ expect(jsParams.genericMethod<JSObject>(_jsObject), _jsObject);
+ expect(jsParams.genericMethod<JSArray>(jsArray), jsArray);
}
diff --git a/tests/lib_2/js/export/static_interop_mock/type_parameter_static_test.dart b/tests/lib_2/js/export/static_interop_mock/type_parameter_static_test.dart
new file mode 100644
index 0000000..9d933ce
--- /dev/null
+++ b/tests/lib_2/js/export/static_interop_mock/type_parameter_static_test.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that `createStaticInteropMock` correctly instantiates to bounds for type
+// parameters.
+
+import 'dart:js_interop';
+import 'package:js/js_util.dart';
+
+import 'functional_test_lib.dart';
+
+@JSExport()
+class Valid {
+ JSArray superMethod(JSAny param) => throw UnimplementedError();
+
+ JSArray get getSet => throw UnimplementedError();
+ set getSet(JSAny val) => throw UnimplementedError();
+ JSArray method(JSAny param) => throw UnimplementedError();
+ Params interopTypeMethod(Supertype param) => throw UnimplementedError();
+ JSArray genericMethod(JSAny param) => throw UnimplementedError();
+}
+
+@JSExport()
+class Invalid {
+ JSAny superMethod(JSArray param) => throw UnimplementedError();
+
+ @JSExport('getSet') // Rename to avoid getter/setter type conflicts.
+ JSAny get getter => throw UnimplementedError();
+ set getSet(JSArray val) => throw UnimplementedError();
+ JSAny method(JSArray param) => throw UnimplementedError();
+ Supersupertype interopTypeMethod(Params param) => throw UnimplementedError();
+ JSAny genericMethod(JSArray param) => throw UnimplementedError();
+}
+
+@JSExport()
+class InvalidContravariant {
+ JSArray superMethod(JSArray param) => throw UnimplementedError();
+
+ JSArray get getSet => throw UnimplementedError();
+ set getSet(JSArray val) => throw UnimplementedError();
+ JSArray method(JSArray param) => throw UnimplementedError();
+ Params interopTypeMethod(Params param) => throw UnimplementedError();
+ JSArray genericMethod(JSArray param) => throw UnimplementedError();
+}
+
+@JSExport()
+class InvalidCovariant {
+ JSAny superMethod(JSAny param) => throw UnimplementedError();
+
+ JSAny get getSet => throw UnimplementedError();
+ set getSet(JSAny val) => throw UnimplementedError();
+ JSAny method(JSAny param) => throw UnimplementedError();
+ Supersupertype interopTypeMethod(Supersupertype param) =>
+ throw UnimplementedError();
+ JSAny genericMethod(JSAny param) => throw UnimplementedError();
+}
+
+void main() {
+ createStaticInteropMock<
+//^
+// [web] Type argument 'Params<JSArray, Params<JSObject, Supertype<JSObject>>>' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.
+// [web] Type argument 'ParamsImpl<JSArray, JSArray, Params<JSObject, Supertype<JSObject>>>' has type parameters that do not match their bound. createStaticInteropMock requires instantiating all type parameters to their bound to ensure mocking conformance.
+ Params<JSArray, Params>,
+ ParamsImpl<JSArray, JSArray, Params>>(
+ ParamsImpl<JSArray, JSArray, Params>());
+ createStaticInteropMock<Params<JSObject, Supertype>,
+ ParamsImpl<JSObject, JSObject, Supertype>>(
+ ParamsImpl<JSObject, JSObject, Supertype>());
+
+ // Note that this is fine, but might fail at runtime due to runtime covariant
+ // checks. This is no different than casting a `List<JSObject>` to `List` and
+ // trying to add a `JSString`. On the JS backends, this will fail, and on
+ // dart2wasm, this will succeed because all JS types get erased to JSValue.
+ createStaticInteropMock<Params, ParamsImpl>(
+ ParamsImpl<JSArray, JSArray, Supertype>());
+ createStaticInteropMock<Params, ParamsImpl>(ParamsImpl());
+ createStaticInteropMock<Params, Valid>(Valid());
+
+ createStaticInteropMock<Params, Invalid>(Invalid());
+//^
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet (FunctionType(JSObject Function())), ParamsExtension.getSet= (FunctionType(void Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'Invalid' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+ createStaticInteropMock<Params, InvalidContravariant>(InvalidContravariant());
+//^
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidContravariant' has a getter, but does not have a setter to implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet= (FunctionType(void Function(JSObject))).
+ createStaticInteropMock<Params, InvalidCovariant>(InvalidCovariant());
+//^
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'genericMethod': ParamsExtension.genericMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'interopTypeMethod': ParamsExtension.interopTypeMethod (FunctionType(Supertype<JSObject> Function(Supertype<JSObject>))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'method': ParamsExtension.method (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' does not have any members that implement any of the following extension member(s) with export name 'superMethod': SupertypeExtension.superMethod (FunctionType(JSObject Function(JSObject))).
+// [web] Dart class 'InvalidCovariant' has a setter, but does not have a getter to implement any of the following extension member(s) with export name 'getSet': ParamsExtension.getSet (FunctionType(JSObject Function())).
+}
diff --git a/tests/lib_2/js/export/validation_test.dart b/tests/lib_2/js/export/validation_test.dart
index af2fab8..5577778 100644
--- a/tests/lib_2/js/export/validation_test.dart
+++ b/tests/lib_2/js/export/validation_test.dart
@@ -179,9 +179,37 @@
createDartExport(ClassWithValue());
}
+// `JSExport` classes can't export methods that define type parameters as those
+// type parameters will never be instantiated through interop. Class type
+// parameters are okay, however.
+@JSExport()
+class GenericAll {
+// ^
+// [web] Class 'GenericAll' has no exportable members in the class or the inheritance chain.
+ void defineTypeParam<T extends int>() {}
+ T useTypeParam<T extends Object>(T t) => t;
+}
+
+class GenericSome<U> {
+ @JSExport()
+ void defineTypeParam<T extends int>() {}
+ // ^
+ // [web] Member 'defineTypeParam' is not a concrete instance member or declares type parameters, and therefore can't be exported.
+ T useTypeParam<T extends Object>(T t) => t;
+ @JSExport()
+ U useClassParam(U u) => u;
+}
+
+void testClassWithGenerics() {
+ createDartExport(GenericAll());
+ createDartExport(GenericSome());
+ createDartExport(GenericSome<int>());
+}
+
void main() {
testNumberOfExports();
testUseDartInterface();
testCollisions();
testClassExportWithValue();
+ testClassWithGenerics();
}