diff --git a/pkg/compiler/lib/src/elements/types.dart b/pkg/compiler/lib/src/elements/types.dart index 022604e73f0..ce038a92bdb 100644 --- a/pkg/compiler/lib/src/elements/types.dart +++ b/pkg/compiler/lib/src/elements/types.dart @@ -2263,4 +2263,14 @@ abstract class DartTypes { } return type; } + + bool canAssignGenericFunctionTo(DartType type) { + type = type.withoutNullability; + return type is FunctionType && type.typeVariables.isNotEmpty || + isSubtype(commonElements.functionType, type) || + type is FutureOrType && canAssignGenericFunctionTo(type.typeArgument) || + type is TypeVariableType && + canAssignGenericFunctionTo(getTypeVariableBound(type.element)) || + type is FunctionTypeVariable && canAssignGenericFunctionTo(type.bound); + } } diff --git a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart index 62a9699f982..d633266ca0d 100644 --- a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart +++ b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart @@ -126,7 +126,11 @@ class ClassNode extends RtiNode { String get kind => 'class'; } -class MethodNode extends RtiNode { +abstract class CallableNode extends RtiNode { + bool selectorApplies(Selector selector, BuiltWorld world); +} + +class MethodNode extends CallableNode { final Entity function; final ParameterStructure parameterStructure; final bool isCallTarget; @@ -139,7 +143,8 @@ class MethodNode extends RtiNode { @override Entity get entity => function; - bool selectorApplies(Selector selector) { + @override + bool selectorApplies(Selector selector, BuiltWorld world) { if (isNoSuchMethod) return true; return (isCallTarget && selector.isClosureCall || instanceName == selector.memberName) && @@ -162,10 +167,37 @@ class MethodNode extends RtiNode { } } +class CallablePropertyNode extends CallableNode { + final MemberEntity property; + final DartType type; + + CallablePropertyNode(this.property, this.type); + + @override + Entity get entity => property; + + @override + String get kind => 'callable-property'; + + @override + bool selectorApplies(Selector selector, BuiltWorld world) { + if (world.annotationsData.getParameterCheckPolicy(property).isTrusted) + return false; + if (property.memberName != selector.memberName) return false; + if (type is FunctionType && + !selector.callStructure + .signatureApplies(ParameterStructure.fromType(type))) return false; + return true; + } + + @override + String toString() => 'CallablePropertyNode(property=$property)'; +} + class TypeVariableTests { - List _nodes = []; - Map _classes = {}; - Map _methods = {}; + Map _classes = {}; + Map _methods = {}; + Map _callableProperties = {}; Map> _appliedSelectorMap; Map> _instantiationMap; @@ -293,11 +325,7 @@ class TypeVariableTests { } ClassNode _getClassNode(ClassEntity cls) { - return _classes.putIfAbsent(cls, () { - ClassNode node = new ClassNode(cls); - _nodes.add(node); - return node; - }); + return _classes.putIfAbsent(cls, () => ClassNode(cls)); } MethodNode _getMethodNode(ElementEnvironment elementEnvironment, @@ -325,11 +353,15 @@ class TypeVariableTests { elementEnvironment.getLocalFunctionType(function)); node = new MethodNode(function, parameterStructure, isCallTarget: true); } - _nodes.add(node); return node; }); } + CallablePropertyNode _getCallablePropertyNode( + MemberEntity property, DartType type) => + _callableProperties.putIfAbsent( + property, () => CallablePropertyNode(property, type)); + void _setupDependencies( ElementEnvironment elementEnvironment, CommonElements commonElements, @@ -431,9 +463,8 @@ class TypeVariableTests { world.forEachDynamicTypeArgument( (Selector selector, Iterable typeArguments) { - void processEntity(Entity entity) { - MethodNode node = _getMethodNode(elementEnvironment, world, entity); - if (node.selectorApplies(selector)) { + void processCallableNode(CallableNode node) { + if (node.selectorApplies(selector, world)) { for (DartType type in typeArguments) { // Register that if `node.entity` needs type arguments then so do // the entities that declare type variables occurring in [type]. @@ -442,10 +473,21 @@ class TypeVariableTests { } } - world.forEachGenericInstanceMethod(processEntity); - world.genericLocalFunctions.forEach(processEntity); - world.closurizedStatics.forEach(processEntity); - world.userNoSuchMethods.forEach(processEntity); + void processMethod(Entity entity) { + MethodNode node = _getMethodNode(elementEnvironment, world, entity); + processCallableNode(node); + } + + void processCallableProperty(MemberEntity entity, DartType type) { + CallablePropertyNode node = _getCallablePropertyNode(entity, type); + processCallableNode(node); + } + + world.forEachGenericInstanceMethod(processMethod); + world.genericLocalFunctions.forEach(processMethod); + world.closurizedStatics.forEach(processMethod); + world.userNoSuchMethods.forEach(processMethod); + world.genericCallableProperties.forEach(processCallableProperty); }); for (GenericInstantiation instantiation in genericInstantiations) { @@ -627,11 +669,14 @@ class TypeVariableTests { world.forEachDynamicTypeArgument( (Selector selector, Iterable typeArguments) { - for (MethodNode node in _methods.values) { - if (node.selectorApplies(selector)) { + for (CallableNode node in [ + ..._methods.values, + ..._callableProperties.values + ]) { + if (node.selectorApplies(selector, world)) { if (forRtiNeeds) { _appliedSelectorMap - .putIfAbsent(selector, () => new Set()) + .putIfAbsent(selector, () => {}) .add(node.entity); } if (node.hasTest) { @@ -824,18 +869,14 @@ class RuntimeTypesNeedImpl implements RuntimeTypesNeed { sink.end(tag); } - bool checkClass(covariant ClassEntity cls) => true; - @override bool classNeedsTypeArguments(ClassEntity cls) { - assert(checkClass(cls)); if (!_elementEnvironment.isGenericClass(cls)) return false; return classesNeedingTypeArguments.contains(cls); } @override bool classHasErasedTypeArguments(ClassEntity cls) { - assert(checkClass(cls)); if (!_elementEnvironment.isGenericClass(cls)) return false; return !classesNeedingTypeArguments.contains(cls); } @@ -1273,7 +1314,8 @@ class RuntimeTypesNeedBuilderImpl implements RuntimeTypesNeedBuilder { typeVariableTests .forEachAppliedSelector((Selector selector, Set targets) { for (Entity target in targets) { - if (methodsNeedingTypeArguments.contains(target) || + if (target is MemberEntity && (target.isField || target.isGetter) || + methodsNeedingTypeArguments.contains(target) || localFunctionsNeedingTypeArguments.contains(target)) { selectorsNeedingTypeArguments.add(selector); if (retainDataForTesting) { diff --git a/pkg/compiler/lib/src/kernel/kernel_world.dart b/pkg/compiler/lib/src/kernel/kernel_world.dart index f7fab57f666..09475e4377c 100644 --- a/pkg/compiler/lib/src/kernel/kernel_world.dart +++ b/pkg/compiler/lib/src/kernel/kernel_world.dart @@ -233,4 +233,28 @@ class KClosedWorldImpl implements KClosedWorld { } return _closurizedStaticsCache; } + + Map _genericCallablePropertiesCache; + + @override + Map get genericCallableProperties { + if (_genericCallablePropertiesCache == null) { + _genericCallablePropertiesCache = {}; + liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) { + if (usage.hasRead) { + DartType type; + if (member.isField) { + type = elementEnvironment.getFieldType(member); + } else if (member.isGetter) { + type = elementEnvironment.getFunctionType(member).returnType; + } + if (type == null) return; + if (dartTypes.canAssignGenericFunctionTo(type)) { + _genericCallablePropertiesCache[member] = type; + } + } + }); + } + return _genericCallablePropertiesCache; + } } diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart index e234deb3443..d58a3b05c24 100644 --- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart +++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart @@ -10,6 +10,7 @@ import '../common_elements.dart'; import '../constants/values.dart'; import '../elements/entities.dart'; import '../elements/types.dart'; +import '../js_backend/annotations.dart' show AnnotationsData; import '../js_backend/interceptor_data.dart' show OneShotInterceptorData; import '../js_backend/native_data.dart' show NativeBasicData; import '../js_model/elements.dart'; @@ -668,6 +669,9 @@ class CodegenWorldImpl implements CodegenWorld { _staticTypeArgumentDependencies = staticTypeArgumentDependencies, _dynamicTypeArgumentDependencies = dynamicTypeArgumentDependencies; + @override + AnnotationsData get annotationsData => _closedWorld.annotationsData; + @override void forEachStaticField(void Function(FieldEntity) f) { bool failure = false; @@ -778,6 +782,32 @@ class CodegenWorldImpl implements CodegenWorld { return _closurizedStaticsCache; } + Map _genericCallablePropertiesCache; + + @override + Map get genericCallableProperties { + if (_genericCallablePropertiesCache == null) { + _genericCallablePropertiesCache = {}; + _liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) { + if (usage.hasRead) { + DartType type; + if (member.isField) { + type = _closedWorld.elementEnvironment.getFieldType(member); + } else if (member.isGetter) { + type = _closedWorld.elementEnvironment + .getFunctionType(member) + .returnType; + } + if (type == null) return; + if (_closedWorld.dartTypes.canAssignGenericFunctionTo(type)) { + _genericCallablePropertiesCache[member] = type; + } + } + }); + } + return _genericCallablePropertiesCache; + } + @override void forEachStaticTypeArgument( void f(Entity function, Set typeArguments)) { diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart index fa8d2bc5926..5032a425f02 100644 --- a/pkg/compiler/lib/src/world.dart +++ b/pkg/compiler/lib/src/world.dart @@ -256,12 +256,17 @@ abstract class BuiltWorld { /// Static or top level methods that are closurized. Iterable get closurizedStatics; + /// Properties (fields and getters) which can be called as generic functions. + Map get genericCallableProperties; + /// Type variables used as type literals. Iterable get typeVariableTypeLiterals; /// Live user-defined 'noSuchMethod' implementations. Iterable get userNoSuchMethods; + AnnotationsData get annotationsData; + /// Calls [f] for each live generic instance methods. void forEachGenericInstanceMethod(void Function(FunctionEntity) f); @@ -306,6 +311,7 @@ abstract class KClosedWorld implements BuiltWorld { RuntimeTypesNeed get rtiNeed; NoSuchMethodData get noSuchMethodData; + @override AnnotationsData get annotationsData; /// Set of live closurized members whose signatures reference type variables. diff --git a/tests/dart2js_2/41449a_test.dart b/tests/dart2js_2/41449a_test.dart index 435ac7ba740..0fa945d031e 100644 --- a/tests/dart2js_2/41449a_test.dart +++ b/tests/dart2js_2/41449a_test.dart @@ -29,6 +29,13 @@ class B2 implements AAA { dynamic get foo => _arr.first; } +class B3 implements AAA { + final dynamic __foo; + B3(this.__foo); + dynamic get _foo => __foo; + dynamic get foo => _foo; +} + @pragma('dart2js:noInline') test1(AAA a, String expected) { // call-through getter 'foo' with one type argument. @@ -44,7 +51,9 @@ test2(AAA a, String expected) { main() { test1(B1(

() => '$P'), 'int'); test1(B2(() => '$Q'), 'num'); + test1(B3(() => '$R'), 'double'); test2(B1(() => '$A $B'), 'int num'); test2(B2(() => '$X $Y'), 'num int'); + test2(B3(() => '$C $D'), 'double String'); } diff --git a/tests/dart2js_2/41449b_test.dart b/tests/dart2js_2/41449b_test.dart index f7bf8e552b2..07e65f3d43f 100644 --- a/tests/dart2js_2/41449b_test.dart +++ b/tests/dart2js_2/41449b_test.dart @@ -29,6 +29,13 @@ class B2 implements AAA { dynamic get foo => _arr.first; } +class B3 implements AAA { + final dynamic __foo; + B3(this.__foo); + dynamic get _foo => __foo; + dynamic get foo => _foo; +} + @pragma('dart2js:noInline') test1(AAA a, String expected) { // call-through getter 'foo' with one type argument. @@ -44,7 +51,9 @@ test2(AAA a, String expected) { main() { test1(B1(

() => '$P'), 'int'); test1(B2(() => '$Q'), 'num'); + test1(B3(() => '$R'), 'double'); test2(B1(() => '$A $B'), 'int num'); test2(B2(() => '$X $Y'), 'num int'); + test2(B3(() => '$C $D'), 'double String'); }