[dart2js] Implementation of extractTypeVariables
Change-Id: I80272dade861e30440f64fe36c1bf4bb5cd2eb8a
Reviewed-on: https://dart-review.googlesource.com/54625
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 5675a1c..ff7d0d7 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -1129,6 +1129,10 @@
FunctionEntity get convertRtiToRuntimeType =>
_findHelperFunction('convertRtiToRuntimeType');
+ FunctionEntity _extractTypeArguments;
+ FunctionEntity get extractTypeArguments => _extractTypeArguments ??=
+ _findLibraryMember(internalLibrary, 'extractTypeArguments');
+
FunctionEntity get toStringForNativeObject =>
_findHelperFunction('toStringForNativeObject');
diff --git a/pkg/compiler/lib/src/kernel/element_map.dart b/pkg/compiler/lib/src/kernel/element_map.dart
index f00dd89..fe468dd 100644
--- a/pkg/compiler/lib/src/kernel/element_map.dart
+++ b/pkg/compiler/lib/src/kernel/element_map.dart
@@ -125,6 +125,7 @@
/// Interface that translates between Kernel IR nodes and entities used for
/// computing the [WorldImpact] for members.
abstract class KernelToElementMapForImpact extends KernelToElementMap {
+ ElementEnvironment get elementEnvironment;
NativeBasicData get nativeBasicData;
/// Adds libraries in [component] to the set of libraries.
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 1438d60..6836904 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -3203,6 +3203,13 @@
return;
}
FunctionEntity function = _elementMap.getMember(target);
+
+ if (options.strongMode &&
+ function == _commonElements.extractTypeArguments &&
+ handleExtractTypeArguments(node, sourceInformation)) {
+ return;
+ }
+
TypeMask typeMask = _typeInferenceMap.getReturnTypeOf(function);
List<DartType> typeArguments =
@@ -3405,6 +3412,69 @@
}
}
+ /// Replace calls to `extractTypeArguments` with equivalent code. Returns
+ /// `true` if `extractTypeArguments` is handled.
+ bool handleExtractTypeArguments(
+ ir.StaticInvocation invocation, SourceInformation sourceInformation) {
+ // Expand calls as follows:
+ //
+ // r = extractTypeArguments<Map>(e, f)
+ // -->
+ // interceptor = getInterceptor(e);
+ // T1 = getRuntimeTypeArgumentIntercepted(interceptor, e, 'Map', 0);
+ // T2 = getRuntimeTypeArgumentIntercepted(interceptor, e, 'Map', 1);
+ // r = f<T1, T2>();
+ //
+ // TODO(sra): Should we add a check before the variable extraction? We could
+ // add a type check (which would permit `null`), or add an is-check with an
+ // explicit throw.
+
+ if (invocation.arguments.positional.length != 2) return false;
+ if (invocation.arguments.named.isNotEmpty) return false;
+ var types = invocation.arguments.types;
+ if (types.length != 1) return false;
+
+ // The type should be a single type name.
+ ir.DartType type = types.first;
+ DartType typeValue =
+ localsHandler.substInContext(_elementMap.getDartType(type));
+ if (typeValue is! InterfaceType) return false;
+ InterfaceType interfaceType = typeValue;
+ if (!interfaceType.treatAsRaw) return false;
+
+ ClassEntity cls = interfaceType.element;
+ InterfaceType thisType = _elementMap.elementEnvironment.getThisType(cls);
+
+ List<HInstruction> arguments =
+ _visitPositionalArguments(invocation.arguments);
+
+ HInstruction object = arguments[0];
+ HInstruction closure = arguments[1];
+ HInstruction interceptor = _interceptorFor(object, sourceInformation);
+
+ List<HInstruction> inputs = <HInstruction>[closure];
+ List<DartType> typeArguments = <DartType>[];
+
+ thisType.typeArguments.forEach((_typeVariable) {
+ TypeVariableType variable = _typeVariable;
+ typeArguments.add(variable);
+ HInstruction readType = new HTypeInfoReadVariable.intercepted(
+ variable, interceptor, object, commonMasks.dynamicType);
+ add(readType);
+ inputs.add(readType);
+ });
+
+ // TODO(sra): In compliance mode, insert a check that [closure] is a
+ // function of N type arguments.
+
+ Selector selector =
+ new Selector.callClosure(0, const <String>[], typeArguments.length);
+ push(new HInvokeClosure(
+ selector, inputs, commonMasks.dynamicType, typeArguments));
+
+ return true;
+ }
+
void handleInvokeStaticForeign(
ir.StaticInvocation invocation, ir.Procedure target) {
String name = target.name.name;
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index 735c58e..7b93861 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -397,6 +397,11 @@
handleNew(node, node.target, isConst: node.isConst);
} else {
FunctionEntity target = elementMap.getMethod(node.target);
+ if (_options.strongMode &&
+ target == commonElements.extractTypeArguments) {
+ _handleExtractTypeArguments(node, target);
+ return;
+ }
List<DartType> typeArguments = _visitArguments(node.arguments);
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
target, elementMap.getCallStructure(node.arguments), typeArguments));
@@ -426,6 +431,35 @@
}
}
+ void _handleExtractTypeArguments(
+ ir.StaticInvocation node, FunctionEntity target) {
+ // extractTypeArguments<Map>(obj, fn) has additional impacts:
+ //
+ // 1. All classes implementing Map need to carry type arguments (similar
+ // to checking `o is Map<K, V>`).
+ //
+ // 2. There is an invocation of fn with some number of type arguments.
+ //
+ List<DartType> typeArguments = _visitArguments(node.arguments);
+ impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
+ target, elementMap.getCallStructure(node.arguments), typeArguments));
+
+ if (typeArguments.length != 1) return;
+ DartType matchedType = typeArguments.first;
+
+ if (matchedType is! InterfaceType) return;
+ InterfaceType interfaceType = matchedType;
+ ClassEntity cls = interfaceType.element;
+ InterfaceType thisType = elementMap.elementEnvironment.getThisType(cls);
+
+ impactBuilder.registerTypeUse(new TypeUse.isCheck(thisType));
+
+ Selector selector = new Selector.callClosure(
+ 0, const <String>[], thisType.typeArguments.length);
+ impactBuilder.registerDynamicUse(
+ new ConstrainedDynamicUse(selector, null, thisType.typeArguments));
+ }
+
@override
void visitStaticGet(ir.StaticGet node) {
ir.Member target = node.target;
diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart
index 263d4ac..3d03614 100644
--- a/pkg/compiler/lib/src/universe/selector.dart
+++ b/pkg/compiler/lib/src/universe/selector.dart
@@ -170,9 +170,10 @@
factory Selector.call(Name name, CallStructure callStructure) =>
new Selector(SelectorKind.CALL, name, callStructure);
- factory Selector.callClosure(int arity, [List<String> namedArguments]) =>
+ factory Selector.callClosure(int arity,
+ [List<String> namedArguments, int typeArgumentCount = 0]) =>
new Selector(SelectorKind.CALL, Names.call,
- new CallStructure(arity, namedArguments));
+ new CallStructure(arity, namedArguments, typeArgumentCount));
factory Selector.callClosureFrom(Selector selector) =>
new Selector(SelectorKind.CALL, Names.call, selector.callStructure);
diff --git a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
index fddf0aa..f10fb5b 100644
--- a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
@@ -5,9 +5,9 @@
import 'dart:core' hide Symbol;
import 'dart:core' as core;
import 'dart:_js_primitives' show printString;
-import 'dart:_js_helper' show patch;
+import 'dart:_js_helper' show patch, NoInline;
import 'dart:_interceptors' show JSArray;
-import 'dart:_foreign_helper' show JS;
+import 'dart:_foreign_helper' show JS, JS_GET_FLAG;
@patch
class Symbol implements core.Symbol {
@@ -49,10 +49,18 @@
}
@patch
+@NoInline()
Object extractTypeArguments<T>(T instance, Function extract) {
- // TODO(31371): Implement this correctly for Dart 2.0.
+ // In Dart 2.0 this function is recognized and replaced with calls to
+ // js_runtime.
+ if (JS_GET_FLAG('STRONG_MODE')) throw new UnimplementedError();
+
// In Dart 1.0, instantiating the generic with dynamic (which this does),
// gives you an object that can be used anywhere a more specific type is
// expected, so this works for now.
+
+ // This call to [extract] is also required for Dart 2.0 to model that the
+ // function is called and the returned value flows to the result of
+ // extractTypeArguments.
return extract();
}
diff --git a/sdk/lib/_internal/js_runtime/lib/js_rti.dart b/sdk/lib/_internal/js_runtime/lib/js_rti.dart
index 9a0627c..44df4af 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_rti.dart
@@ -575,7 +575,6 @@
// Interceptor is needed for JSArray and native classes.
// TODO(sra): It could be a more specialized interceptor since [object] is not
// `null` or a primitive.
- // TODO(9586): Move type info for static functions onto an interceptor.
var interceptor = getInterceptor(object);
var isSubclass = getField(interceptor, isField);
// When we read the field and it is not there, [isSubclass] will be `null`.
@@ -592,7 +591,6 @@
// Interceptor is needed for JSArray and native classes.
// TODO(sra): It could be a more specialized interceptor since [object] is not
// `null` or a primitive.
- // TODO(9586): Move type info for static functions onto an interceptor.
var interceptor = getInterceptor(object);
var isSubclass = getField(interceptor, isField);
// When we read the field and it is not there, [isSubclass] will be `null`.
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index c44b6e2..3d8c648 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -189,6 +189,9 @@
typevariable_typedef_test: Fail, OK # Tests expected output of Type.toString().
[ $compiler == dart2js && !$strong ]
+extract_type_arguments_1_test: RuntimeError # Uses function type variables
+extract_type_arguments_2_test: RuntimeError # Uses function type variables
+extract_type_arguments_3_test: RuntimeError # Uses function type variables
generic_method_dynamic_is_test: RuntimeError # Test against function type variables is only supported in strong mode.
generic_method_dynamic_type_test: SkipByDesign # Requires strong mode support for function type variables.
generic_method_static_is_test: RuntimeError # Test against function type variables is only supported in strong mode.
diff --git a/tests/compiler/dart2js_extra/extract_type_arguments_1_test.dart b/tests/compiler/dart2js_extra/extract_type_arguments_1_test.dart
new file mode 100644
index 0000000..74d5081
--- /dev/null
+++ b/tests/compiler/dart2js_extra/extract_type_arguments_1_test.dart
@@ -0,0 +1,18 @@
+// 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=--omit-implicit-checks
+
+import 'package:expect/expect.dart';
+import 'dart:_internal' show extractTypeArguments;
+
+class C<T> {}
+
+main() {
+ test(new C<int>(), int);
+ test(new C<String>(), String);
+}
+
+test(dynamic a, T) {
+ Expect.equals(T, extractTypeArguments<C>(a, <T>() => T));
+}
diff --git a/tests/compiler/dart2js_extra/extract_type_arguments_2_test.dart b/tests/compiler/dart2js_extra/extract_type_arguments_2_test.dart
new file mode 100644
index 0000000..bf8c2f9
--- /dev/null
+++ b/tests/compiler/dart2js_extra/extract_type_arguments_2_test.dart
@@ -0,0 +1,20 @@
+// 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=--omit-implicit-checks
+
+import 'package:expect/expect.dart';
+import 'dart:_internal' show extractTypeArguments;
+
+class CC<A, B, C, D, E> {}
+
+class X {}
+
+main() {
+ test(new CC<X, X, X, X, int>(), int);
+ test(new CC<X, X, X, X, String>(), String);
+}
+
+test(dynamic a, T) {
+ Expect.equals(T, extractTypeArguments<CC>(a, <_1, _2, _3, _4, T>() => T));
+}
diff --git a/tests/compiler/dart2js_extra/extract_type_arguments_3_test.dart b/tests/compiler/dart2js_extra/extract_type_arguments_3_test.dart
new file mode 100644
index 0000000..10fa04a
--- /dev/null
+++ b/tests/compiler/dart2js_extra/extract_type_arguments_3_test.dart
@@ -0,0 +1,11 @@
+// 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=--omit-implicit-checks
+
+import 'package:expect/expect.dart';
+import 'dart:_internal' show extractTypeArguments;
+
+main() {
+ Expect.equals(int, extractTypeArguments<List>('hello'.codeUnits, <T>() => T));
+}
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index 4be878c..66550b4 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -876,7 +876,6 @@
checked_setter2_test: RuntimeError # Issue 31128
checked_setter_test: RuntimeError # Issue 31128
const_dynamic_type_literal_test/03: Pass # but it shouldn't until we fix issue 17207
-extract_type_arguments_test: Crash # Issue 31371
function_propagation_test: RuntimeError
generic_test/01: MissingCompileTimeError # front end does not validate `extends`
instantiate_tearoff_of_call_test: RuntimeError
@@ -1016,7 +1015,6 @@
external_test/20: MissingRuntimeError
external_test/21: CompileTimeError
external_test/24: CompileTimeError
-extract_type_arguments_test: RuntimeError # Issue 31371
f_bounded_quantification4_test: RuntimeError
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError
@@ -1567,7 +1565,6 @@
external_test/20: MissingRuntimeError
external_test/21: CompileTimeError
external_test/24: CompileTimeError
-extract_type_arguments_test: RuntimeError # Issue 31371
f_bounded_quantification4_test: RuntimeError # Issue 12605
f_bounded_quantification_test/01: MissingCompileTimeError
f_bounded_quantification_test/02: MissingCompileTimeError