Share non-generic signatures through init.types

Change-Id: Ie132bbe805780022c91e5578c8dc4636c3c4cc8b
Reviewed-on: https://dart-review.googlesource.com/56671
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Johnni Winther 2018-05-28 07:40:24 +00:00 committed by commit-bot@chromium.org
parent b79e06630c
commit 6b7f3d5f54
20 changed files with 337 additions and 27 deletions

View file

@ -1118,6 +1118,9 @@ class CommonElements {
FunctionEntity get instantiatedGenericFunctionType =>
_findHelperFunction('instantiatedGenericFunctionType');
FunctionEntity get extractFunctionTypeObjectFromInternal =>
_findHelperFunction('extractFunctionTypeObjectFromInternal');
// From dart:_internal
ClassEntity _symbolImplementationClass;

View file

@ -774,6 +774,7 @@ class BackendImpacts {
_commonElements.instantiate2,
_commonElements.instantiate3,
_commonElements.instantiatedGenericFunctionType,
_commonElements.extractFunctionTypeObjectFromInternal,
], instantiatedClasses: [
_commonElements.instantiation1Class,
_commonElements.instantiation2Class,

View file

@ -155,6 +155,7 @@ class CodegenEnqueuerListener extends EnqueuerListener {
_customElementsAnalysis.registerTypeConstant(representedType.element);
}
} else if (constant is InstantiationConstantValue) {
// TODO(johnniwinther): Register these using `BackendImpact`.
impactBuilder.registerTypeUse(new TypeUse.instantiation(
_elementEnvironment
.getThisType(_commonElements.instantiation1Class)));
@ -167,6 +168,9 @@ class CodegenEnqueuerListener extends EnqueuerListener {
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
_commonElements.instantiatedGenericFunctionType,
CallStructure.TWO_ARGS));
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
_commonElements.extractFunctionTypeObjectFromInternal,
CallStructure.ONE_ARG));
}
}

View file

@ -1571,6 +1571,26 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
typeVariableTestsForTesting = typeVariableTests;
}
/*print(typeVariableTests.dump());
print('------------------------------------------------------------------');
print('classesNeedingTypeArguments:');
classesNeedingTypeArguments.forEach((e) => print(' $e'));
print('------------------------------------------------------------------');
print('methodsNeedingSignature:');
methodsNeedingSignature.forEach((e) => print(' $e'));
print('------------------------------------------------------------------');
print('methodsNeedingTypeArguments:');
methodsNeedingTypeArguments.forEach((e) => print(' $e'));
print('------------------------------------------------------------------');
print('localFunctionsNeedingSignature:');
localFunctionsNeedingSignature.forEach((e) => print(' $e'));
print('------------------------------------------------------------------');
print('localFunctionsNeedingTypeArguments:');
localFunctionsNeedingTypeArguments.forEach((e) => print(' $e'));
print('------------------------------------------------------------------');
print('selectorsNeedingTypeArguments:');
selectorsNeedingTypeArguments.forEach((e) => print(' $e'));*/
return _createRuntimeTypesNeed(
_elementEnvironment,
closedWorld.backendUsage,

View file

@ -104,18 +104,19 @@ class InstantiationStubGenerator {
/// ```
/// $signature:: function() {
/// return H.instantiatedGenericFunctionType(
/// this._genericClosure.$signature(),
/// H.extractFunctionTypeObjectFromInternal(this._genericClosure),
/// this.$ti);
/// }
/// ```
ParameterStubMethod _generateSignatureStub(FieldEntity functionField) {
jsAst.Name operatorSignature = _namer.asName(_namer.operatorSignature);
jsAst.Fun function = js('function() { return #(this.#.#(), this.#); }', [
jsAst.Fun function = js('function() { return #(#(this.#), this.#); }', [
_emitter.staticFunctionAccess(
_commonElements.instantiatedGenericFunctionType),
_emitter.staticFunctionAccess(
_commonElements.extractFunctionTypeObjectFromInternal),
_namer.fieldPropertyName(functionField),
operatorSignature,
_namer.rtiFieldJsName,
]);
// TODO(sra): Generate source information for stub that has no member.

View file

@ -101,6 +101,7 @@ class RuntimeTypeGenerator {
final RuntimeTypesEncoder _rtiEncoder;
final JsInteropAnalysis _jsInteropAnalysis;
final bool _strongMode;
final _TypeContainedInOutputUnitVisitor _outputUnitVisitor;
RuntimeTypeGenerator(
this._commonElements,
@ -111,7 +112,9 @@ class RuntimeTypeGenerator {
this._rtiChecks,
this._rtiEncoder,
this._jsInteropAnalysis,
this._strongMode);
this._strongMode)
: _outputUnitVisitor = new _TypeContainedInOutputUnitVisitor(
_commonElements, _outputUnitData);
/**
* Generate "is tests" for [cls] itself, and the "is tests" for the
@ -145,19 +148,28 @@ class RuntimeTypeGenerator {
// signatures. We either need them for mirrors or because [type] is
// potentially a subtype of a checked function. Currently we eagerly
// generate a function type index or signature for all callable classes.
if (storeFunctionTypeInMetadata && !type.containsTypeVariables) {
// TODO(johnniwinther,efortuna): Should we use the scheme for Dart 2?
// TODO(sigmund): use output unit of `method` (Issue #31032)
OutputUnit outputUnit = _outputUnitData.mainOutputUnit;
result.functionTypeIndex =
emitterTask.metadataCollector.reifyType(type, outputUnit);
jsAst.Expression functionTypeIndex;
if (!type.containsTypeVariables) {
// TODO(sigmund): use output unit of [method] when the classes mentioned
// in [type] aren't in the main output unit. (Issue #31032)
OutputUnit mainOutputUnit = _outputUnitData.mainOutputUnit;
if (_outputUnitVisitor.isTypeContainedIn(type, mainOutputUnit)) {
functionTypeIndex =
emitterTask.metadataCollector.reifyType(type, mainOutputUnit);
}
}
if (storeFunctionTypeInMetadata && functionTypeIndex != null) {
result.functionTypeIndex = functionTypeIndex;
} else {
jsAst.Expression encoding =
generatedCode[classFunctionType.signatureFunction];
if (classFunctionType.signatureFunction != null) {
// Use precomputed signature function if live.
} else {
assert(!_strongMode);
if (_strongMode) {
if (classFunctionType.signatureFunction == null) {
// The signature function isn't live.
return;
}
encoding = functionTypeIndex ?? encoding;
} else if (encoding == null) {
// Generate the signature on the fly. This is only supported for
// Dart 1.
@ -240,3 +252,84 @@ class RuntimeTypeGenerator {
}
}
}
/// Visitor that checks whether a type is contained within one output unit.
class _TypeContainedInOutputUnitVisitor
implements DartTypeVisitor<bool, OutputUnit> {
final CommonElements _commonElements;
final OutputUnitData _outputUnitData;
_TypeContainedInOutputUnitVisitor(this._commonElements, this._outputUnitData);
/// Returns `true` if all classes mentioned in [type] are in [outputUnit].
bool isTypeContainedIn(DartType type, OutputUnit outputUnit) =>
visit(type, outputUnit);
@override
bool visit(DartType type, OutputUnit argument) => type.accept(this, argument);
bool visitList(List<DartType> types, OutputUnit argument) {
for (DartType type in types) {
if (!visit(type, argument)) {
return false;
}
}
return true;
}
@override
bool visitFutureOrType(FutureOrType type, OutputUnit argument) {
if (_outputUnitData.outputUnitForClass(_commonElements.functionClass) !=
argument) {
return false;
}
return visit(type.typeArgument, argument);
}
@override
bool visitDynamicType(DynamicType type, OutputUnit argument) => true;
@override
bool visitTypedefType(TypedefType type, OutputUnit argument) {
return visit(type.unaliased, argument);
}
@override
bool visitInterfaceType(InterfaceType type, OutputUnit argument) {
if (_outputUnitData.outputUnitForClass(type.element) != argument) {
return false;
}
return visitList(type.typeArguments, argument);
}
@override
bool visitFunctionType(FunctionType type, OutputUnit argument) {
bool result = visit(type.returnType, argument) &&
visitList(type.parameterTypes, argument) &&
visitList(type.optionalParameterTypes, argument) &&
visitList(type.namedParameterTypes, argument);
if (!result) return false;
for (FunctionTypeVariable typeVariable in type.typeVariables) {
if (!visit(typeVariable.bound, argument)) {
return false;
}
}
return true;
}
@override
bool visitFunctionTypeVariable(
FunctionTypeVariable type, OutputUnit argument) {
return true;
}
@override
bool visitTypeVariableType(TypeVariableType type, OutputUnit argument) {
return false;
}
@override
bool visitVoidType(VoidType type, OutputUnit argument) {
return true;
}
}

View file

@ -3447,10 +3447,20 @@ listSuperNativeTypeCast(value, property) {
extractFunctionTypeObjectFrom(o) {
var interceptor = getInterceptor(o);
return extractFunctionTypeObjectFromInternal(interceptor);
}
extractFunctionTypeObjectFromInternal(o) {
var signatureName = JS_GET_NAME(JsGetName.SIGNATURE_NAME);
return JS('bool', '# in #', signatureName, interceptor)
? JS('', '#[#]()', interceptor, signatureName)
: null;
if (JS('bool', '# in #', signatureName, o)) {
var signature = JS('', '#[#]', o, signatureName);
if (JS('bool', 'typeof # == "number"', signature)) {
return getType(signature);
} else {
return JS('', '#[#]()', o, signatureName);
}
}
return null;
}
functionTypeTest(value, functionTypeRti) {

View file

@ -859,10 +859,8 @@ bool checkSubtypeOfRuntimeType(o, t) {
if (isDartFunctionType(t)) {
// Functions are treated specially and have their type information stored
// directly in the instance.
var targetSignatureFunction =
getField(o, '${JS_GET_NAME(JsGetName.SIGNATURE_NAME)}');
if (targetSignatureFunction == null) return false;
type = invokeOn(targetSignatureFunction, o, null);
type = extractFunctionTypeObjectFromInternal(o);
if (type == null) return false;
return isFunctionSubtype(type, t);
}
return isSubtype(type, t);

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib1.loadLibrary();
Expect.isTrue(lib1.method1() is int Function(int));
Expect.isFalse(lib1.method1() is String Function(String));
await lib2.loadLibrary();
Expect.isFalse(lib2.method2() is int Function(int));
Expect.isTrue(lib2.method2() is String Function(String));
}

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib2.loadLibrary();
Expect.isFalse(lib2.method2() is int Function(int));
Expect.isTrue(lib2.method2() is String Function(String));
await lib1.loadLibrary();
Expect.isTrue(lib1.method1() is int Function(int));
Expect.isFalse(lib1.method1() is String Function(String));
}

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib1.loadLibrary();
Expect.isTrue(lib1.method3() is Object Function(Null));
Expect.isFalse(lib1.method3() is Object Function(Null, Null));
await lib2.loadLibrary();
Expect.isFalse(lib2.method4() is Object Function(Null));
Expect.isTrue(lib2.method4() is Object Function(Null, Null));
}

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib2.loadLibrary();
Expect.isFalse(lib2.method4() is Object Function(Null));
Expect.isTrue(lib2.method4() is Object Function(Null, Null));
await lib1.loadLibrary();
Expect.isTrue(lib1.method3() is Object Function(Null));
Expect.isFalse(lib1.method3() is Object Function(Null, Null));
}

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib1.loadLibrary();
Expect.isTrue(lib1.test3(lib1.method3()));
Expect.isFalse(lib1.method3() is Object Function(String));
await lib2.loadLibrary();
Expect.isFalse(lib2.method4() is Object Function(String, String));
Expect.isTrue(lib2.test4(lib2.method4()));
}

View file

@ -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=--strong
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib2.loadLibrary();
Expect.isFalse(lib2.method4() is Object Function(String, String));
Expect.isTrue(lib2.test4(lib2.method4()));
await lib1.loadLibrary();
Expect.isTrue(lib1.test3(lib1.method3()));
Expect.isFalse(lib1.method3() is Object Function(String));
}

View file

@ -0,0 +1,22 @@
// 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
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib1.loadLibrary();
Expect.isTrue(lib1.method1() is int Function(int));
Expect.isFalse(lib1.method1() is String Function(String));
Expect.isTrue(lib1.method5 is Object Function(Null, String, int));
Expect.isFalse(lib1.method5 is Object Function(Null, int, String));
await lib2.loadLibrary();
Expect.isFalse(lib2.method2() is int Function(int));
Expect.isTrue(lib2.method2() is String Function(String));
Expect.isFalse(lib2.method6 is Object Function(Null, String, int));
Expect.isTrue(lib2.method6 is Object Function(Null, int, String));
}

View file

@ -0,0 +1,22 @@
// 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
import 'package:expect/expect.dart';
import 'deferred_function_types_lib1.dart' deferred as lib1;
import 'deferred_function_types_lib2.dart' deferred as lib2;
main() async {
await lib2.loadLibrary();
Expect.isFalse(lib2.method2() is int Function(int));
Expect.isTrue(lib2.method2() is String Function(String));
Expect.isFalse(lib2.method6 is Object Function(Null, String, int));
Expect.isTrue(lib2.method6 is Object Function(Null, int, String));
await lib1.loadLibrary();
Expect.isTrue(lib1.method1() is int Function(int));
Expect.isFalse(lib1.method1() is String Function(String));
Expect.isTrue(lib1.method5 is Object Function(Null, String, int));
Expect.isFalse(lib1.method5 is Object Function(Null, int, String));
}

View file

@ -0,0 +1,17 @@
// 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.
method1() {
return (int i) => i;
}
class Class1 {}
method3() {
return (Class1 c) => c;
}
test3(o) => o is Class1 Function(Class1);
method5(Class1 c, String s, int i) {}

View file

@ -0,0 +1,17 @@
// 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.
method2() {
return (String s) => s;
}
class Class2 {}
method4() {
return (Class2 c1, Class2 c2) => c1;
}
test4(o) => o is Class2 Function(Class2, Class2);
method6(Class2 c, int i, String s) {}

View file

@ -196,10 +196,6 @@ list_test/none: Crash # Unsupported operation: Unsupported type parameter type n
map_test: Crash # tests/corelib_2/map_test.dart:903:7: Internal problem: Unhandled Null in installDefaultConstructor.
symbol_reserved_word_test/03: RuntimeError # Issue 19972, new Symbol('void') should be allowed.
[ $compiler == dart2js && $fast_startup ]
apply3_test: RuntimeError
dynamic_nosuchmethod_test: RuntimeError
[ $compiler == dart2js && $fast_startup && $fasta && $strong ]
cast_test: RuntimeError
error_stack_trace1_test: RuntimeError

View file

@ -486,7 +486,6 @@ stacktrace_rethrow_error_test/none: RuntimeError # Issue 12698
stacktrace_rethrow_error_test/withtraceparameter: RuntimeError # Issue 12698
stacktrace_rethrow_nonerror_test: RuntimeError # Issue 12698
stacktrace_test: RuntimeError # Issue 12698
super_call4_test: Crash # NoSuchMethodError: The getter 'thisLocal' was called on null.
switch_bad_case_test/01: MissingCompileTimeError
switch_bad_case_test/02: MissingCompileTimeError
switch_case_test/00: MissingCompileTimeError
@ -808,7 +807,6 @@ string_interpolation_and_buffer_test: RuntimeError
string_split_test: CompileTimeError
string_supertype_checked_test: CompileTimeError
super_bound_closure_test/none: CompileTimeError
super_call4_test: Crash # NoSuchMethodError: The getter 'thisLocal' was called on null.
super_no_such_method1_test: CompileTimeError
super_no_such_method2_test: CompileTimeError
super_no_such_method3_test: CompileTimeError