[dart2js] Ensure type arguments are passed when invoking a generic

function property.

Change-Id: I387977e2f1fb7732d94331b7a97cceeec767aaae
Bug: https://github.com/dart-lang/sdk/issues/41449
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151301
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Mayank Patke <fishythefish@google.com>
This commit is contained in:
Mayank Patke
2020-06-24 20:12:39 +00:00
committed by commit-bot@chromium.org
parent e3fb81af38
commit cf62339751
7 changed files with 156 additions and 26 deletions
+10
View File
@@ -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);
}
}
@@ -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<RtiNode> _nodes = <RtiNode>[];
Map<ClassEntity, ClassNode> _classes = <ClassEntity, ClassNode>{};
Map<Entity, MethodNode> _methods = <Entity, MethodNode>{};
Map<ClassEntity, ClassNode> _classes = {};
Map<Entity, MethodNode> _methods = {};
Map<MemberEntity, CallablePropertyNode> _callableProperties = {};
Map<Selector, Set<Entity>> _appliedSelectorMap;
Map<GenericInstantiation, Set<Entity>> _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<DartType> 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<DartType> 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<Entity>())
.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<Entity> 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) {
@@ -233,4 +233,28 @@ class KClosedWorldImpl implements KClosedWorld {
}
return _closurizedStaticsCache;
}
Map<MemberEntity, DartType> _genericCallablePropertiesCache;
@override
Map<MemberEntity, DartType> 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;
}
}
@@ -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<MemberEntity, DartType> _genericCallablePropertiesCache;
@override
Map<MemberEntity, DartType> 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<DartType> typeArguments)) {
+6
View File
@@ -256,12 +256,17 @@ abstract class BuiltWorld {
/// Static or top level methods that are closurized.
Iterable<FunctionEntity> get closurizedStatics;
/// Properties (fields and getters) which can be called as generic functions.
Map<MemberEntity, DartType> get genericCallableProperties;
/// Type variables used as type literals.
Iterable<TypeVariableType> get typeVariableTypeLiterals;
/// Live user-defined 'noSuchMethod' implementations.
Iterable<FunctionEntity> 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.
+9
View File
@@ -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<T>(AAA a, String expected) {
// call-through getter 'foo' with one type argument.
@@ -44,7 +51,9 @@ test2<U, V>(AAA a, String expected) {
main() {
test1<int>(B1(<P>() => '$P'), 'int');
test1<num>(B2(<Q>() => '$Q'), 'num');
test1<double>(B3(<R>() => '$R'), 'double');
test2<int, num>(B1(<A, B>() => '$A $B'), 'int num');
test2<num, int>(B2(<X, Y>() => '$X $Y'), 'num int');
test2<double, String>(B3(<C, D>() => '$C $D'), 'double String');
}
+9
View File
@@ -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<T>(AAA a, String expected) {
// call-through getter 'foo' with one type argument.
@@ -44,7 +51,9 @@ test2<U, V>(AAA a, String expected) {
main() {
test1<int>(B1(<P>() => '$P'), 'int');
test1<num>(B2(<Q>() => '$Q'), 'num');
test1<double>(B3(<R>() => '$R'), 'double');
test2<int, num>(B1(<A, B>() => '$A $B'), 'int num');
test2<num, int>(B2(<X, Y>() => '$X $Y'), 'num int');
test2<double, String>(B3(<C, D>() => '$C $D'), 'double String');
}