Handle generic jsinterop classes as type arguments.
Change-Id: I89d8a77d632cf6d4f8adffa84f042a1c03a79f26
Reviewed-on: https://dart-review.googlesource.com/56665
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 1281eb9..743ef5c 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -2146,12 +2146,21 @@
jsAst.Expression visitInterfaceType(InterfaceType type, Emitter emitter) {
jsAst.Expression name = getJavaScriptClassName(type.element, emitter);
- if (_nativeData.isJsInteropClass(type.element)) {
- return getJsInteropTypeArguments(type.typeArguments.length, name: name);
+ jsAst.Expression result;
+ if (type.treatAsRaw) {
+ result = name;
+ } else {
+ // Visit all type arguments. This is done even for jsinterop classes to
+ // enforce the invariant that [onVariable] is called for each type
+ // variable in the type.
+ result = visitList(type.typeArguments, emitter, head: name);
+ if (_nativeData.isJsInteropClass(type.element)) {
+ // Replace type arguments of generic jsinterop classes with 'any' type.
+ result =
+ getJsInteropTypeArguments(type.typeArguments.length, name: name);
+ }
}
- return type.treatAsRaw
- ? name
- : visitList(type.typeArguments, emitter, head: name);
+ return result;
}
jsAst.Expression visitList(List<DartType> types, Emitter emitter,
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 37288e6..671f01b 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -3092,6 +3092,7 @@
}
void visitTypeInfoExpression(HTypeInfoExpression node) {
+ DartType type = node.dartType;
List<js.Expression> arguments = <js.Expression>[];
for (HInstruction input in node.inputs) {
use(input);
@@ -3103,19 +3104,19 @@
int index = 0;
js.Expression result = _rtiEncoder.getTypeRepresentation(
_emitter.emitter,
- node.dartType,
+ type,
(TypeVariableType variable) => arguments[index++]);
assert(
index == node.inputs.length,
- "Not all input is read for type ${node.dartType}: "
+ "Not all input is read for type ${type}: "
"$index of ${node.inputs}.");
push(result);
return;
case TypeInfoExpressionKind.INSTANCE:
// We expect only flat types for the INSTANCE representation.
- assert((node.dartType as InterfaceType).typeArguments.length ==
- arguments.length);
+ assert(
+ (type as InterfaceType).typeArguments.length == arguments.length);
_registry.registerInstantiatedClass(_commonElements.listClass);
push(new js.ArrayInitializer(arguments)
.withSourceInformation(node.sourceInformation));
diff --git a/tests/compiler/dart2js/rti/emission/jsinterop_generic_factory_args.dart b/tests/compiler/dart2js/rti/emission/jsinterop_generic_factory_args.dart
new file mode 100644
index 0000000..de9bc8b
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/jsinterop_generic_factory_args.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2018, 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.
+
+// dart2jsOptions=--strong
+
+@JS()
+library foo;
+
+/*class: global#JavaScriptObject:checks=[$asA,$isA]*/
+
+import 'package:expect/expect.dart';
+import 'package:js/js.dart';
+
+/*kernel.class: A:checkedTypeArgument,checks=[],typeArgument*/
+/*strong.class: A:checkedInstance,checkedTypeArgument,checks=[],typeArgument*/
+@JS()
+@anonymous
+class A<T> {
+ external factory A();
+}
+
+/*class: Class1:checks=[],instance*/
+class Class1<T> {
+ method() {
+ // This caused and assertion failure in codegen.
+ test1(new List<A<T>>.from([]));
+ }
+}
+
+/*class: Class2:checks=[],instance*/
+class Class2<T> {
+ method() {
+ // This caused and assertion failure in codegen.
+ test2(new List<A<T> Function()>.from([]));
+ }
+}
+
+main() {
+ new Class1<int>().method();
+ new Class1<String>().method();
+ new Class2<int>().method();
+ new Class2<String>().method();
+}
+
+test1(o) {
+ Expect.isTrue(o is List<A<int>>, "Expected $o to be List<A<int>>");
+ Expect.isTrue(o is List<A<String>>, "Expected $o to be List<A<String>>");
+}
+
+test2(o) {
+ Expect.isTrue(o is List<A<int> Function()>,
+ "Expected $o to be List<A<int> Function()>");
+ Expect.isTrue(o is List<A<String> Function()>,
+ "Expected $o to be List<A<String> Function()>");
+}