Handle generic factory constructors in kernel_impact

R=het@google.com

Review URL: https://codereview.chromium.org/2341263002 .
This commit is contained in:
Johnni Winther 2016-09-19 11:35:56 +02:00
parent 9f3b5d8680
commit e24939f8d6
6 changed files with 80 additions and 8 deletions

View file

@ -619,7 +619,9 @@ class ConstructorResolver extends CommonResolverVisitor<ConstructorResult> {
// This is not really resolving a type-annotation, but the name of the // This is not really resolving a type-annotation, but the name of the
// constructor. Therefore we allow deferred types. // constructor. Therefore we allow deferred types.
DartType type = resolver.resolveTypeAnnotation(node, DartType type = resolver.resolveTypeAnnotation(node,
malformedIsError: inConstContext, deferredIsMalformed: false); malformedIsError: inConstContext,
deferredIsMalformed: false,
registerCheckedModeCheck: false);
Send send = node.typeName.asSend(); Send send = node.typeName.asSend();
PrefixElement prefix; PrefixElement prefix;
if (send != null) { if (send != null) {

View file

@ -4063,11 +4063,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
} }
DartType resolveTypeAnnotation(TypeAnnotation node, DartType resolveTypeAnnotation(TypeAnnotation node,
{bool malformedIsError: false, bool deferredIsMalformed: true}) { {bool malformedIsError: false,
bool deferredIsMalformed: true,
bool registerCheckedModeCheck: true}) {
DartType type = typeResolver.resolveTypeAnnotation(this, node, DartType type = typeResolver.resolveTypeAnnotation(this, node,
malformedIsError: malformedIsError, malformedIsError: malformedIsError,
deferredIsMalformed: deferredIsMalformed); deferredIsMalformed: deferredIsMalformed);
if (!type.isDynamic) { if (registerCheckedModeCheck && !type.isDynamic) {
registry.registerTypeUse(new TypeUse.checkedModeCheck(type)); registry.registerTypeUse(new TypeUse.checkedModeCheck(type));
} }
return type; return type;

View file

@ -176,7 +176,8 @@ class TypeResolver {
} else { } else {
type = new InterfaceType( type = new InterfaceType(
cls.declaration, arguments.toList(growable: false)); cls.declaration, arguments.toList(growable: false));
addTypeVariableBoundsCheck = true; addTypeVariableBoundsCheck =
arguments.any((DartType type) => !type.isDynamic);
} }
} }
} else if (element.isTypedef) { } else if (element.isTypedef) {
@ -195,7 +196,8 @@ class TypeResolver {
type = typdef.rawType; type = typdef.rawType;
} else { } else {
type = new TypedefType(typdef, arguments.toList(growable: false)); type = new TypedefType(typdef, arguments.toList(growable: false));
addTypeVariableBoundsCheck = true; addTypeVariableBoundsCheck =
arguments.any((DartType type) => !type.isDynamic);
} }
} }
} else if (element.isTypeVariable) { } else if (element.isTypeVariable) {

View file

@ -173,6 +173,10 @@ class KernelAstAdapter {
DartType getDartType(ir.DartType type) { DartType getDartType(ir.DartType type) {
return type.accept(_typeConverter); return type.accept(_typeConverter);
} }
List<DartType> getDartTypes(List<ir.DartType> types) {
return types.map(getDartType).toList();
}
} }
class DartTypeConverter extends ir.DartTypeVisitor<DartType> { class DartTypeConverter extends ir.DartTypeVisitor<DartType> {

View file

@ -195,8 +195,44 @@ class KernelImpactBuilder extends ir.Visitor {
void visitStaticInvocation(ir.StaticInvocation invocation) { void visitStaticInvocation(ir.StaticInvocation invocation) {
_visitArguments(invocation.arguments); _visitArguments(invocation.arguments);
Element target = astAdapter.getElement(invocation.target).declaration; Element target = astAdapter.getElement(invocation.target).declaration;
impactBuilder.registerStaticUse(new StaticUse.staticInvoke( if (target.isFactoryConstructor) {
target, astAdapter.getCallStructure(invocation.arguments))); impactBuilder.registerStaticUse(new StaticUse.constructorInvoke(
target, astAdapter.getCallStructure(invocation.arguments)));
// TODO(johnniwinther): We should not mark the type as instantiated but
// rather follow the type arguments directly.
//
// Consider this:
//
// abstract class A<T> {
// factory A.regular() => new B<T>();
// factory A.redirect() = B<T>;
// }
//
// class B<T> implements A<T> {}
//
// main() {
// print(new A<int>.regular() is B<int>);
// print(new A<String>.redirect() is B<String>);
// }
//
// To track that B is actually instantiated as B<int> and B<String> we
// need to follow the type arguments passed to A.regular and A.redirect
// to B. Currently, we only do this soundly if we register A<int> and
// A<String> as instantiated. We should instead register that A.T is
// instantiated as int and String.
ClassElement cls =
astAdapter.getElement(invocation.target.enclosingClass);
List<DartType> typeArguments =
astAdapter.getDartTypes(invocation.arguments.types);
impactBuilder.registerTypeUse(
new TypeUse.instantiation(new InterfaceType(cls, typeArguments)));
if (typeArguments.any((DartType type) => !type.isDynamic)) {
impactBuilder.registerFeature(Feature.TYPE_VARIABLE_BOUNDS_CHECK);
}
} else {
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
target, astAdapter.getCallStructure(invocation.arguments)));
}
} }
@override @override

View file

@ -18,6 +18,8 @@ import '../serialization/test_helper.dart';
const Map<String, String> SOURCE = const <String, String>{ const Map<String, String> SOURCE = const <String, String>{
'main.dart': ''' 'main.dart': '''
import 'helper.dart';
main() { main() {
testEmpty(); testEmpty();
testNull(); testNull();
@ -58,6 +60,10 @@ main() {
testInvokeIndexSet(null); testInvokeIndexSet(null);
testAssert(); testAssert();
testAssertWithMessage(); testAssertWithMessage();
testFactoryInvoke();
testFactoryInvokeGeneric();
testFactoryInvokeGenericRaw();
testFactoryInvokeGenericDynamic();
} }
testEmpty() {} testEmpty() {}
@ -154,7 +160,27 @@ testAssert() {
testAssertWithMessage() { testAssertWithMessage() {
assert(true, 'ok'); assert(true, 'ok');
} }
''' testFactoryInvoke() {
new Class.fact();
}
testFactoryInvokeGeneric() {
new GenericClass<int, String>.fact();
}
testFactoryInvokeGenericRaw() {
new GenericClass.fact();
}
testFactoryInvokeGenericDynamic() {
new GenericClass<dynamic, dynamic>.fact();
}
''',
'helper.dart': '''
class Class {
factory Class.fact() => null;
}
class GenericClass<X, Y> {
factory GenericClass.fact() => null;
}
''',
}; };
main(List<String> args) { main(List<String> args) {