blob: d34cb0103db4ad71039db72e8710ab667869b415 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart';
import 'package:front_end/src/fasta/type_inference/interface_resolver.dart';
import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/testing/mock_sdk_component.dart';
import 'package:kernel/type_algebra.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InterfaceResolverTest);
});
}
@reflectiveTest
class InterfaceResolverTest {
final Library testLib;
final Component component;
final CoreTypes coreTypes;
ClassHierarchy cachedClassHierarchy;
TypeSchemaEnvironment cachedTypeEnvironment;
InterfaceResolver cachedInterfaceResolver;
InterfaceResolverTest()
: this._(new Library(Uri.parse('org-dartlang:///test.dart'), name: 'lib'),
createMockSdkComponent());
InterfaceResolverTest._(this.testLib, Component component)
: component = component..libraries.add(testLib..parent = component),
coreTypes = new CoreTypes(component);
ClassHierarchy get classHierarchy {
return cachedClassHierarchy ??= new ClassHierarchy(component);
}
TypeSchemaEnvironment get typeEnvironment {
return cachedTypeEnvironment ??=
new TypeSchemaEnvironment(coreTypes, classHierarchy);
}
InterfaceResolver get interfaceResolver {
return cachedInterfaceResolver ??=
new InterfaceResolver(null, typeEnvironment, null);
}
InterfaceType get intType => coreTypes.intClass.rawType;
Class get listClass => coreTypes.listClass;
InterfaceType get numType => coreTypes.numClass.rawType;
Class get objectClass => coreTypes.objectClass;
InterfaceType get objectType => objectClass.rawType;
void checkCandidate(Procedure procedure, bool setter) {
var class_ = makeClass(procedures: [procedure]);
var candidate = getCandidate(class_, setter);
expect(candidate, same(procedure));
}
void checkCandidateOrder(Class class_, Member member) {
// Check that InterfaceResolver prioritizes [member]
var candidates = getCandidates(class_, false);
expect(candidates[0], same(member));
// Check that both implementations of [ClassHierarchy] prioritize [member]
// ahead of [other]
void check(ClassHierarchy classHierarchy) {
var interfaceMember =
classHierarchy.getInterfaceMember(class_, member.name);
expect(interfaceMember, same(member));
}
check(new ClassHierarchy(component));
}
Procedure getCandidate(Class class_, bool setter) {
var candidates = getCandidates(class_, setter);
expect(candidates, hasLength(1));
return candidates[0];
}
List<Procedure> getCandidates(Class class_, bool setters) =>
interfaceResolver.getCandidates(class_, setters);
ForwardingNode getForwardingNode(Class class_, bool setter) {
var forwardingNodes = getForwardingNodes(class_, setter);
expect(forwardingNodes, hasLength(1));
return forwardingNodes[0];
}
List<ForwardingNode> getForwardingNodes(Class class_, bool setters) {
var forwardingNodes = <ForwardingNode>[];
var candidates = getCandidates(class_, setters);
InterfaceResolver.forEachApiMember(candidates,
(int start, int end, Name name) {
forwardingNodes.add(new ForwardingNode(interfaceResolver, null, class_,
name, candidates[start].kind, candidates, start, end));
});
return forwardingNodes;
}
Member getStubTarget(Procedure stub) {
var body = stub.function.body;
if (body == null) return null;
if (body is ReturnStatement) {
var expression = body.expression;
if (expression is SuperMethodInvocation) {
return expression.interfaceTarget;
} else if (expression is SuperPropertySet) {
return expression.interfaceTarget;
} else {
fail('Unexpected expression type: ${expression.runtimeType}');
}
} else {
fail('Unexpected body type: ${body.runtimeType}');
}
}
Class makeClass(
{String name,
Supertype supertype,
Supertype mixedInType,
List<TypeParameter> typeParameters,
List<Supertype> implementedTypes,
List<Procedure> procedures,
List<Field> fields}) {
resetInterfaceResolver();
var class_ = new ShadowClass(
name: name ?? 'C',
supertype: supertype ?? objectClass.asThisSupertype,
mixedInType: mixedInType,
typeParameters: typeParameters,
implementedTypes: implementedTypes,
procedures: procedures,
fields: fields);
testLib.addClass(class_);
return class_;
}
Procedure makeEmptyMethod(
{ProcedureKind kind: ProcedureKind.Method,
String name: 'foo',
List<TypeParameter> typeParameters,
List<VariableDeclaration> positionalParameters,
List<VariableDeclaration> namedParameters,
int requiredParameterCount,
DartType returnType: const VoidType(),
bool isAbstract: false}) {
var body = isAbstract ? null : new ReturnStatement(new NullLiteral());
var function = new FunctionNode(body,
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: requiredParameterCount,
returnType: returnType);
return new ShadowProcedure(new Name(name), kind, function, false,
isAbstract: isAbstract);
}
Field makeField(
{String name: 'foo',
DartType type: const DynamicType(),
bool isFinal: false}) {
return new Field(new Name(name), type: type, isFinal: isFinal);
}
Procedure makeForwardingStub(Procedure method, bool setter,
{Substitution substitution}) {
var a = makeClass(name: 'A', procedures: [method]);
var b = makeClass(name: 'B', supertype: a.asThisSupertype);
var node = getForwardingNode(b, setter);
var stub = ForwardingNode.createForwardingStubForTesting(
node, substitution ?? Substitution.empty, method);
ForwardingNode.createForwardingImplIfNeededForTesting(node, stub.function);
return stub;
}
Procedure makeGetter(
{String name: 'foo', DartType getterType: const DynamicType()}) {
var body = new ReturnStatement(new NullLiteral());
var function = new FunctionNode(body, returnType: getterType);
return new ShadowProcedure(
new Name(name), ProcedureKind.Getter, function, false);
}
Procedure makeSetter(
{String name: 'foo',
DartType setterType: const DynamicType(),
bool isCovariant: false}) {
var parameter = new VariableDeclarationJudgment('value', 0,
type: setterType, isCovariant: isCovariant);
var body = new Block([]);
var function = new FunctionNode(body,
positionalParameters: [parameter], returnType: const VoidType());
return new ShadowProcedure(
new Name(name), ProcedureKind.Setter, function, false);
}
void resetInterfaceResolver() {
cachedClassHierarchy = null;
cachedTypeEnvironment = null;
cachedInterfaceResolver = null;
}
void test_candidate_for_field_getter() {
var field = makeField();
var class_ = makeClass(fields: [field]);
var candidate = getCandidate(class_, false);
expect(candidate, new isInstanceOf<SyntheticAccessor>());
expect(candidate.parent, same(class_));
expect(candidate.name, field.name);
expect(candidate.kind, ProcedureKind.Getter);
expect(candidate.function.positionalParameters, isEmpty);
expect(candidate.function.namedParameters, isEmpty);
expect(candidate.function.typeParameters, isEmpty);
}
void test_candidate_for_field_setter() {
var field = makeField();
var class_ = makeClass(fields: [field]);
var candidate = getCandidate(class_, true);
expect(candidate, new isInstanceOf<SyntheticAccessor>());
expect(candidate.parent, same(class_));
expect(candidate.name, field.name);
expect(candidate.kind, ProcedureKind.Setter);
expect(candidate.function.positionalParameters, hasLength(1));
expect(candidate.function.positionalParameters[0].name, '_');
expect(candidate.function.namedParameters, isEmpty);
expect(candidate.function.typeParameters, isEmpty);
expect(candidate.function.returnType, const VoidType());
}
void test_candidate_for_getter() {
var function = new FunctionNode(null);
var getter = new ShadowProcedure(
new Name('foo'), ProcedureKind.Getter, function, false);
checkCandidate(getter, false);
}
void test_candidate_for_method() {
checkCandidate(makeEmptyMethod(), false);
}
void test_candidate_for_setter() {
var parameter = new VariableDeclarationJudgment('value', 0);
var function = new FunctionNode(null,
positionalParameters: [parameter], returnType: const VoidType());
var setter = new ShadowProcedure(
new Name('foo'), ProcedureKind.Setter, function, false);
checkCandidate(setter, true);
}
void test_candidate_from_interface() {
var method = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [method]);
var b = makeClass(name: 'B', implementedTypes: [a.asThisSupertype]);
var candidate = getCandidate(b, false);
expect(candidate, same(method));
}
void test_candidate_from_mixin() {
var method = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [method]);
var b = makeClass(name: 'B', mixedInType: a.asThisSupertype);
var candidate = getCandidate(b, false);
expect(candidate, same(method));
}
void test_candidate_from_superclass() {
var method = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [method]);
var b = makeClass(name: 'B', supertype: a.asThisSupertype);
var candidate = getCandidate(b, false);
expect(candidate, same(method));
}
void test_candidate_order_interfaces() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C', implementedTypes: [a.asThisSupertype, b.asThisSupertype]);
checkCandidateOrder(c, methodA);
}
void test_candidate_order_mixin_before_superclass() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C',
supertype: a.asThisSupertype,
mixedInType: b.asThisSupertype);
checkCandidateOrder(c, methodB);
}
void test_candidate_order_superclass_before_interface() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C',
supertype: a.asThisSupertype,
implementedTypes: [b.asThisSupertype]);
checkCandidateOrder(c, methodA);
}
void test_createForwardingStub_abstract() {
var method = makeEmptyMethod(isAbstract: true);
var stub = makeForwardingStub(method, false);
expect(stub.isAbstract, isTrue);
expect(stub.function.body, isNull);
}
void test_createForwardingStub_getter() {
var getter = makeGetter(getterType: numType);
var stub = makeForwardingStub(getter, false);
expect(stub.name, getter.name);
expect(stub.kind, ProcedureKind.Getter);
expect(stub.function.positionalParameters, isEmpty);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 0);
expect(stub.function.returnType, numType);
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperPropertyGet;
expect(expression.name, getter.name);
expect(expression.interfaceTarget, same(getter));
}
void test_createForwardingStub_getter_for_field() {
var field = makeField(type: numType);
var stub = makeForwardingStub(
InterfaceResolver.makeCandidate(field, false), false);
expect(stub.name, field.name);
expect(stub.kind, ProcedureKind.Getter);
expect(stub.function.positionalParameters, isEmpty);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 0);
expect(stub.function.returnType, numType);
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperPropertyGet;
expect(expression.name, field.name);
expect(expression.interfaceTarget, same(field));
}
void test_createForwardingStub_operator() {
var operator = makeEmptyMethod(
kind: ProcedureKind.Operator,
name: '[]=',
positionalParameters: [
new VariableDeclarationJudgment('index', 0, type: intType),
new VariableDeclarationJudgment('value', 0, type: numType)
]);
var stub = makeForwardingStub(operator, false);
expect(stub.name, operator.name);
expect(stub.kind, ProcedureKind.Operator);
expect(stub.function.positionalParameters, hasLength(2));
expect(stub.function.positionalParameters[0].name,
operator.function.positionalParameters[0].name);
expect(stub.function.positionalParameters[0].type, intType);
expect(stub.function.positionalParameters[1].name,
operator.function.positionalParameters[1].name);
expect(stub.function.positionalParameters[1].type, numType);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 2);
expect(stub.function.returnType, const VoidType());
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperMethodInvocation;
expect(expression.name, operator.name);
expect(expression.interfaceTarget, same(operator));
var arguments = expression.arguments;
expect(arguments.positional, hasLength(2));
expect((arguments.positional[0] as VariableGet).variable,
same(stub.function.positionalParameters[0]));
expect((arguments.positional[1] as VariableGet).variable,
same(stub.function.positionalParameters[1]));
}
void test_createForwardingStub_optionalNamedParameter() {
var parameter = new VariableDeclarationJudgment('x', 0, type: intType);
var method = makeEmptyMethod(namedParameters: [parameter]);
var stub = makeForwardingStub(method, false);
expect(stub.function.namedParameters, hasLength(1));
expect(stub.function.namedParameters[0].name, 'x');
expect(stub.function.namedParameters[0].type, intType);
expect(stub.function.requiredParameterCount, 0);
var arguments = ((stub.function.body as ReturnStatement).expression
as SuperMethodInvocation)
.arguments;
expect(arguments.named, hasLength(1));
expect(arguments.named[0].name, 'x');
expect((arguments.named[0].value as VariableGet).variable,
same(stub.function.namedParameters[0]));
}
void test_createForwardingStub_optionalPositionalParameter() {
var parameter = new VariableDeclarationJudgment('x', 0, type: intType);
var method = makeEmptyMethod(
positionalParameters: [parameter], requiredParameterCount: 0);
var stub = makeForwardingStub(method, false);
expect(stub.function.positionalParameters, hasLength(1));
expect(stub.function.positionalParameters[0].name, 'x');
expect(stub.function.positionalParameters[0].type, intType);
expect(stub.function.requiredParameterCount, 0);
var arguments = ((stub.function.body as ReturnStatement).expression
as SuperMethodInvocation)
.arguments;
expect(arguments.positional, hasLength(1));
expect((arguments.positional[0] as VariableGet).variable,
same(stub.function.positionalParameters[0]));
}
void test_createForwardingStub_requiredParameter() {
var parameter = new VariableDeclarationJudgment('x', 0, type: intType);
var method = makeEmptyMethod(positionalParameters: [parameter]);
var stub = makeForwardingStub(method, false);
expect(stub.function.positionalParameters, hasLength(1));
expect(stub.function.positionalParameters[0].name, 'x');
expect(stub.function.positionalParameters[0].type, intType);
expect(stub.function.requiredParameterCount, 1);
var arguments = ((stub.function.body as ReturnStatement).expression
as SuperMethodInvocation)
.arguments;
expect(arguments.positional, hasLength(1));
expect((arguments.positional[0] as VariableGet).variable,
same(stub.function.positionalParameters[0]));
}
void test_createForwardingStub_setter() {
var setter = makeSetter(setterType: numType);
var stub = makeForwardingStub(setter, true);
expect(stub.name, setter.name);
expect(stub.kind, ProcedureKind.Setter);
expect(stub.function.positionalParameters, hasLength(1));
expect(stub.function.positionalParameters[0].name,
setter.function.positionalParameters[0].name);
expect(stub.function.positionalParameters[0].type, numType);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 1);
expect(stub.function.returnType, const VoidType());
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperPropertySet;
expect(expression.name, setter.name);
expect(expression.interfaceTarget, same(setter));
expect((expression.value as VariableGet).variable,
same(stub.function.positionalParameters[0]));
}
void test_createForwardingStub_setter_for_field() {
var field = makeField(type: numType);
var stub =
makeForwardingStub(InterfaceResolver.makeCandidate(field, true), true);
expect(stub.name, field.name);
expect(stub.kind, ProcedureKind.Setter);
expect(stub.function.positionalParameters, hasLength(1));
expect(stub.function.positionalParameters[0].name, '_');
expect(stub.function.positionalParameters[0].type, numType);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 1);
expect(stub.function.returnType, const VoidType());
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperPropertySet;
expect(expression.name, field.name);
expect(expression.interfaceTarget, same(field));
expect((expression.value as VariableGet).variable,
same(stub.function.positionalParameters[0]));
}
void test_createForwardingStub_simple() {
var method = makeEmptyMethod();
var stub = makeForwardingStub(method, false);
expect(stub.name, method.name);
expect(stub.kind, ProcedureKind.Method);
expect(stub.isAbstract, isFalse);
expect(stub.function.positionalParameters, isEmpty);
expect(stub.function.namedParameters, isEmpty);
expect(stub.function.typeParameters, isEmpty);
expect(stub.function.requiredParameterCount, 0);
expect(stub.function.returnType, const VoidType());
var body = stub.function.body as ReturnStatement;
var expression = body.expression as SuperMethodInvocation;
expect(expression.name, method.name);
expect(expression.interfaceTarget, same(method));
expect(expression.arguments.positional, isEmpty);
expect(expression.arguments.named, isEmpty);
expect(expression.arguments.types, isEmpty);
}
void test_createForwardingStub_substitute() {
// class C<T> { T foo(T x, {T y}); }
var T = new TypeParameter('T', objectType);
var x =
new VariableDeclarationJudgment('x', 0, type: new TypeParameterType(T));
var y =
new VariableDeclarationJudgment('y', 0, type: new TypeParameterType(T));
var method = makeEmptyMethod(
positionalParameters: [x],
namedParameters: [y],
returnType: new TypeParameterType(T));
var substitution = Substitution.fromPairs([T], [intType]);
var stub = makeForwardingStub(method, false, substitution: substitution);
expect(stub.function.positionalParameters[0].type, intType);
expect(stub.function.namedParameters[0].type, intType);
expect(stub.function.returnType, intType);
}
void test_createForwardingStub_typeParameter() {
var typeParameter = new TypeParameter('T', numType);
var method = makeEmptyMethod(typeParameters: [typeParameter]);
var stub = makeForwardingStub(method, false);
expect(stub.function.typeParameters, hasLength(1));
expect(stub.function.typeParameters[0].name, 'T');
expect(stub.function.typeParameters[0].bound, numType);
var arguments = ((stub.function.body as ReturnStatement).expression
as SuperMethodInvocation)
.arguments;
expect(arguments.types, hasLength(1));
var typeArgument = arguments.types[0] as TypeParameterType;
expect(typeArgument.parameter, same(stub.function.typeParameters[0]));
expect(typeArgument.promotedBound, isNull);
}
void test_createForwardingStub_typeParameter_and_substitution() {
// class C<T> { void foo<U>(T x, U y); }
var T = new TypeParameter('T', objectType);
var U = new TypeParameter('U', objectType);
var x =
new VariableDeclarationJudgment('x', 0, type: new TypeParameterType(T));
var y =
new VariableDeclarationJudgment('y', 0, type: new TypeParameterType(U));
var method =
makeEmptyMethod(typeParameters: [U], positionalParameters: [x, y]);
var substitution = Substitution.fromPairs([T], [intType]);
var stub = makeForwardingStub(method, false, substitution: substitution);
expect(stub.function.positionalParameters[0].type, intType);
var stubYType =
stub.function.positionalParameters[1].type as TypeParameterType;
expect(stubYType.parameter, same(stub.function.typeParameters[0]));
}
void test_createForwardingStub_typeParameter_substituteUses() {
// class C { void foo<T>(T x); }
var typeParameter = new TypeParameter('T', objectType);
var param = new VariableDeclarationJudgment('x', 0,
type: new TypeParameterType(typeParameter));
var method = makeEmptyMethod(
typeParameters: [typeParameter], positionalParameters: [param]);
var stub = makeForwardingStub(method, false);
var stubXType =
stub.function.positionalParameters[0].type as TypeParameterType;
expect(stubXType.parameter, same(stub.function.typeParameters[0]));
}
void test_createForwardingStub_typeParameter_substituteUses_fBounded() {
// class C { void foo<T extends List<T>>(T x); }
var typeParameter = new TypeParameter('T', null);
typeParameter.bound =
new InterfaceType(listClass, [new TypeParameterType(typeParameter)]);
var param = new VariableDeclarationJudgment('x', 0,
type: new TypeParameterType(typeParameter));
var method = makeEmptyMethod(
typeParameters: [typeParameter], positionalParameters: [param]);
var stub = makeForwardingStub(method, false);
var stubTypeParameter = stub.function.typeParameters[0];
var stubTypeParameterBound = stubTypeParameter.bound as InterfaceType;
var stubTypeParameterBoundArg =
stubTypeParameterBound.typeArguments[0] as TypeParameterType;
expect(stubTypeParameterBoundArg.parameter, same(stubTypeParameter));
var stubXType =
stub.function.positionalParameters[0].type as TypeParameterType;
expect(stubXType.parameter, same(stubTypeParameter));
}
void test_direct_isGenericCovariant() {
var typeParameter = new TypeParameter('T', objectType);
var u = new TypeParameter('U', new TypeParameterType(typeParameter));
var x = new VariableDeclarationJudgment('x', 0,
type: new TypeParameterType(typeParameter));
var y = new VariableDeclarationJudgment('y', 0,
type: new TypeParameterType(typeParameter));
var method = makeEmptyMethod(
typeParameters: [u], positionalParameters: [x], namedParameters: [y]);
var class_ =
makeClass(typeParameters: [typeParameter], procedures: [method]);
var node = getForwardingNode(class_, false);
ShadowClass.setBuilder(class_, null);
var resolvedMethod = node.finalize();
expect(resolvedMethod, same(method));
expect(u.isGenericCovariantImpl, isTrue);
expect(x.isGenericCovariantImpl, isTrue);
expect(x.isCovariant, isFalse);
expect(y.isGenericCovariantImpl, isTrue);
expect(y.isCovariant, isFalse);
}
void test_direct_isGenericCovariant_field() {
var typeParameter = new TypeParameter('T', objectType);
var field = makeField(type: new TypeParameterType(typeParameter));
var class_ = makeClass(typeParameters: [typeParameter], fields: [field]);
var node = getForwardingNode(class_, true);
ShadowClass.setBuilder(class_, null);
var resolvedAccessor = node.finalize() as SyntheticAccessor;
expect(SyntheticAccessor.getField(resolvedAccessor), same(field));
expect(field.isGenericCovariantImpl, isTrue);
expect(field.isCovariant, isFalse);
}
void test_field_isCovariant_inherited() {
var fieldA = makeField(type: numType)..isCovariant = true;
var fieldB = makeField(type: numType);
var a = makeClass(name: 'A', fields: [fieldA]);
var b = makeClass(
name: 'B', implementedTypes: [a.asThisSupertype], fields: [fieldB]);
var node = getForwardingNode(b, true);
var resolvedAccessor = node.finalize() as SyntheticAccessor;
expect(SyntheticAccessor.getField(resolvedAccessor), same(fieldB));
expect(fieldB.isGenericCovariantImpl, isFalse);
expect(fieldB.isCovariant, isTrue);
}
void test_field_isGenericCovariantImpl_inherited() {
var typeParameter = new TypeParameter('T', objectType);
var fieldA = makeField(type: new TypeParameterType(typeParameter))
..isGenericCovariantImpl = true;
var fieldB = makeField(type: numType);
var a =
makeClass(name: 'A', typeParameters: [typeParameter], fields: [fieldA]);
var b = makeClass(name: 'B', implementedTypes: [
new Supertype(a, [numType])
], fields: [
fieldB
]);
var node = getForwardingNode(b, true);
var resolvedAccessor = node.finalize() as SyntheticAccessor;
expect(SyntheticAccessor.getField(resolvedAccessor), same(fieldB));
expect(fieldB.isGenericCovariantImpl, isTrue);
expect(fieldB.isCovariant, isFalse);
}
void test_forwardingNodes_multiple() {
var methodAf = makeEmptyMethod(name: 'f');
var methodBf = makeEmptyMethod(name: 'f');
var methodAg = makeEmptyMethod(name: 'g');
var methodBg = makeEmptyMethod(name: 'g');
var a = makeClass(name: 'A', procedures: [methodAf, methodAg]);
var b = makeClass(
name: 'B',
supertype: a.asThisSupertype,
procedures: [methodBf, methodBg]);
var forwardingNodes = getForwardingNodes(b, false);
expect(forwardingNodes, hasLength(2));
var nodef = ClassHierarchy.findMemberByName(forwardingNodes, methodAf.name);
var nodeg = ClassHierarchy.findMemberByName(forwardingNodes, methodAg.name);
expect(nodef, isNot(same(nodeg)));
expect(nodef.parent, b);
expect(nodeg.parent, b);
{
var candidates = ForwardingNode.getCandidates(nodef);
expect(candidates, hasLength(2));
expect(candidates[0], same(methodBf));
expect(candidates[1], same(methodAf));
}
{
var candidates = ForwardingNode.getCandidates(nodeg);
expect(candidates, hasLength(2));
expect(candidates[0], same(methodBg));
expect(candidates[1], same(methodAg));
}
}
void test_forwardingNodes_single() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(
name: 'B', supertype: a.asThisSupertype, procedures: [methodB]);
var forwardingNodes = getForwardingNodes(b, false);
expect(forwardingNodes, hasLength(1));
expect(forwardingNodes[0].parent, b);
expect(forwardingNodes[0].name, methodA.name);
var candidates = ForwardingNode.getCandidates(forwardingNodes[0]);
expect(candidates, hasLength(2));
expect(candidates[0], same(methodB));
expect(candidates[1], same(methodA));
}
void test_forwardingStub_isCovariant_inherited() {
var methodA = makeEmptyMethod(positionalParameters: [
new VariableDeclarationJudgment('x', 0, type: numType)
], namedParameters: [
new VariableDeclarationJudgment('y', 0, type: numType)
]);
var methodB = makeEmptyMethod(positionalParameters: [
new VariableDeclarationJudgment('x', 0, type: intType)..isCovariant = true
], namedParameters: [
new VariableDeclarationJudgment('y', 0, type: intType)..isCovariant = true
]);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C',
supertype: a.asThisSupertype,
implementedTypes: [b.asThisSupertype]);
var node = getForwardingNode(c, false);
var stub = node.finalize();
var x = stub.function.positionalParameters[0];
expect(x.isGenericCovariantImpl, isFalse);
expect(x.isCovariant, isTrue);
var y = stub.function.namedParameters[0];
expect(y.isGenericCovariantImpl, isFalse);
expect(y.isCovariant, isTrue);
expect(stub.forwardingStubInterfaceTarget, same(methodA));
expect(getStubTarget(stub), same(methodA));
}
void test_forwardingStub_isGenericCovariantImpl_inherited() {
var methodA = makeEmptyMethod(typeParameters: [
new TypeParameter('U', numType)
], positionalParameters: [
new VariableDeclarationJudgment('x', 0, type: numType)
], namedParameters: [
new VariableDeclarationJudgment('y', 0, type: numType)
]);
var typeParameterB = new TypeParameter('T', objectType);
var methodB = makeEmptyMethod(typeParameters: [
new TypeParameter('U', new TypeParameterType(typeParameterB))
..isGenericCovariantImpl = true
], positionalParameters: [
new VariableDeclarationJudgment('x', 0,
type: new TypeParameterType(typeParameterB))
..isGenericCovariantImpl = true
], namedParameters: [
new VariableDeclarationJudgment('y', 0,
type: new TypeParameterType(typeParameterB))
..isGenericCovariantImpl = true
]);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(
name: 'B', typeParameters: [typeParameterB], procedures: [methodB]);
var c =
makeClass(name: 'C', supertype: a.asThisSupertype, implementedTypes: [
new Supertype(b, [numType])
]);
var node = getForwardingNode(c, false);
var stub = node.finalize();
var u = stub.function.typeParameters[0];
expect(u.isGenericCovariantImpl, isTrue);
var x = stub.function.positionalParameters[0];
expect(x.isGenericCovariantImpl, isTrue);
expect(x.isCovariant, isFalse);
var y = stub.function.namedParameters[0];
expect(y.isGenericCovariantImpl, isTrue);
expect(y.isCovariant, isFalse);
expect(stub.forwardingStubInterfaceTarget, same(methodA));
expect(getStubTarget(stub), same(methodA));
}
void test_interfaceTarget_cascaded() {
var methodC = makeEmptyMethod(positionalParameters: [
new VariableDeclaration('x', type: intType),
new VariableDeclaration('y', type: intType)
]);
var c = makeClass(name: 'C', procedures: [methodC]);
var t = new TypeParameter('T', objectType);
var methodI1 = makeEmptyMethod(positionalParameters: [
new VariableDeclaration('x', type: new TypeParameterType(t))
..isGenericCovariantImpl = true,
new VariableDeclaration('y', type: intType)
]);
var i1 = makeClass(name: 'I1', typeParameters: [t], procedures: [methodI1]);
// Let's say D was previously compiled, so it already has a forwarding stub
var d =
makeClass(name: 'D', supertype: c.asThisSupertype, implementedTypes: [
new Supertype(i1, [intType])
]);
var nodeD = getForwardingNode(d, false);
var methodD = ForwardingNode.createForwardingStubForTesting(
nodeD, Substitution.empty, methodC);
d.addMember(methodD);
ForwardingNode.createForwardingImplIfNeededForTesting(
nodeD, methodD.function);
// To ensure that we don't accidentally make use of information that was
// computed prior to adding the forwarding stub, reset the interface
// resolver.
resetInterfaceResolver();
var u = new TypeParameter('U', objectType);
var methodI2 = makeEmptyMethod(positionalParameters: [
new VariableDeclaration('x', type: intType),
new VariableDeclaration('y', type: new TypeParameterType(u))
..isGenericCovariantImpl = true
]);
var i2 = makeClass(name: 'I2', typeParameters: [u], procedures: [methodI2]);
var e =
makeClass(name: 'E', supertype: d.asThisSupertype, implementedTypes: [
new Supertype(i2, [intType])
]);
var nodeE = getForwardingNode(e, false);
var stub = nodeE.finalize();
expect(stub.forwardingStubInterfaceTarget, same(methodC));
expect(getStubTarget(stub), same(methodC));
}
void test_interfaceTarget_cascaded_setter() {
var setterC = makeSetter(setterType: intType);
var c = makeClass(name: 'C', procedures: [setterC]);
var t = new TypeParameter('T', objectType);
var setterI1 = makeSetter(setterType: new TypeParameterType(t));
var i1 = makeClass(name: 'I1', typeParameters: [t], procedures: [setterI1]);
// Let's say D was previously compiled, so it already has a forwarding stub
var d =
makeClass(name: 'D', supertype: c.asThisSupertype, implementedTypes: [
new Supertype(i1, [intType])
]);
var nodeD = getForwardingNode(d, true);
var setterD = ForwardingNode.createForwardingStubForTesting(
nodeD, Substitution.empty, setterC);
d.addMember(setterD);
ForwardingNode.createForwardingImplIfNeededForTesting(
nodeD, setterD.function);
// To ensure that we don't accidentally make use of information that was
// computed prior to adding the forwarding stub, reset the interface
// resolver.
resetInterfaceResolver();
var setterI2 = makeSetter(setterType: intType, isCovariant: true);
var i2 = makeClass(name: 'I2', procedures: [setterI2]);
var e = makeClass(
name: 'E',
supertype: d.asThisSupertype,
implementedTypes: [i2.asThisSupertype]);
var nodeE = getForwardingNode(e, true);
var stub = nodeE.finalize();
expect(stub.forwardingStubInterfaceTarget, same(setterC));
expect(getStubTarget(stub), same(setterC));
}
void test_interfaceTarget_field() {
var fieldA = makeField(type: numType, isFinal: true);
var fieldB = makeField(type: intType, isFinal: true);
var a = makeClass(name: 'A', fields: [fieldA]);
var b = makeClass(name: 'B', fields: [fieldB]);
var c = makeClass(
name: 'C',
supertype: a.asThisSupertype,
implementedTypes: [b.asThisSupertype]);
var node = getForwardingNode(c, false);
var stub = node.finalize();
expect(stub.forwardingStubInterfaceTarget, same(fieldB));
}
void test_merge_candidates_including_mixin() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var methodC = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(name: 'C', procedures: [methodC]);
var d = makeClass(
name: 'D',
supertype: a.asThisSupertype,
mixedInType: b.asThisSupertype,
implementedTypes: [c.asThisSupertype]);
var candidates = getCandidates(d, false);
expect(candidates, hasLength(3));
expect(candidates[0], same(methodB));
expect(candidates[1], same(methodA));
expect(candidates[2], same(methodC));
}
void test_merge_candidates_not_including_mixin() {
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var methodC = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C',
supertype: a.asThisSupertype,
implementedTypes: [b.asThisSupertype],
procedures: [methodC]);
var candidates = getCandidates(c, false);
expect(candidates, hasLength(3));
expect(candidates[0], same(methodC));
expect(candidates[1], same(methodA));
expect(candidates[2], same(methodB));
}
void test_resolve_directly_declared() {
var parameterA = new VariableDeclarationJudgment('x', 0,
type: objectType, isCovariant: true);
var methodA = makeEmptyMethod(positionalParameters: [parameterA]);
var parameterB = new VariableDeclarationJudgment('x', 0,
type: intType, isCovariant: true);
var methodB = makeEmptyMethod(positionalParameters: [parameterB]);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(
name: 'B', supertype: a.asThisSupertype, procedures: [methodB]);
var node = getForwardingNode(b, false);
expect(node.finalize(), same(methodB));
}
void test_resolve_favor_first() {
// When multiple methods have equivalent types, favor the first one.
var methodA = makeEmptyMethod();
var methodB = makeEmptyMethod();
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C', implementedTypes: [a.asThisSupertype, b.asThisSupertype]);
var node = getForwardingNode(c, false);
expect(node.finalize(), same(methodA));
}
void test_resolve_field() {
var field = makeField();
var a = makeClass(name: 'A', fields: [field]);
var b = makeClass(name: 'B', supertype: a.asThisSupertype);
var node = getForwardingNode(b, false);
var accessor = node.finalize() as SyntheticAccessor;
expect(SyntheticAccessor.getField(accessor), same(field));
}
void test_resolve_first() {
var methodA = makeEmptyMethod(returnType: intType);
var methodB = makeEmptyMethod(returnType: numType);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C', implementedTypes: [a.asThisSupertype, b.asThisSupertype]);
var node = getForwardingNode(c, false);
expect(node.finalize(), same(methodA));
}
void test_resolve_second() {
var methodA = makeEmptyMethod(returnType: numType);
var methodB = makeEmptyMethod(returnType: intType);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(name: 'B', procedures: [methodB]);
var c = makeClass(
name: 'C', implementedTypes: [a.asThisSupertype, b.asThisSupertype]);
var node = getForwardingNode(c, false);
var stub = node.finalize();
expect(stub.forwardingStubInterfaceTarget, same(methodB));
expect(getStubTarget(stub), isNull);
expect(stub.function.returnType, intType);
}
void test_resolve_setters() {
var setterA = makeSetter(setterType: intType);
var setterB = makeSetter(setterType: objectType);
var setterC = makeSetter(setterType: numType);
var a = makeClass(name: 'A', procedures: [setterA]);
var b = makeClass(name: 'B', procedures: [setterB]);
var c = makeClass(name: 'C', procedures: [setterC]);
var d = makeClass(name: 'D', implementedTypes: [
a.asThisSupertype,
b.asThisSupertype,
c.asThisSupertype
]);
var node = getForwardingNode(d, true);
var stub = node.finalize();
expect(stub.forwardingStubInterfaceTarget, same(setterB));
expect(getStubTarget(stub), isNull);
expect(stub.function.positionalParameters[0].type, objectType);
}
void test_resolve_with_added_implementation() {
var methodA = makeEmptyMethod(positionalParameters: [
new VariableDeclarationJudgment('x', 0, type: numType)
]);
var typeParamB = new TypeParameter('T', objectType);
var methodB = makeEmptyMethod(positionalParameters: [
new VariableDeclarationJudgment('x', 0,
type: new TypeParameterType(typeParamB))
..isGenericCovariantImpl = true
]);
var methodC = makeEmptyMethod(positionalParameters: [
new VariableDeclarationJudgment('x', 0, type: numType)
], isAbstract: true);
var a = makeClass(name: 'A', procedures: [methodA]);
var b = makeClass(
name: 'B', typeParameters: [typeParamB], procedures: [methodB]);
var c =
makeClass(name: 'C', supertype: a.asThisSupertype, implementedTypes: [
new Supertype(b, [numType])
], procedures: [
methodC
]);
var node = getForwardingNode(c, false);
expect(methodC.function.body, isNull);
var resolvedMethod = node.finalize();
expect(resolvedMethod, same(methodC));
expect(methodC.function.body, isNotNull);
expect(methodC.forwardingStubInterfaceTarget, isNull);
expect(getStubTarget(methodC), same(methodA));
}
void test_resolve_with_substitutions() {
var typeParamA = new TypeParameter('T', objectType);
var typeParamB = new TypeParameter('T', objectType);
var typeParamC = new TypeParameter('T', objectType);
var methodA =
makeEmptyMethod(returnType: new TypeParameterType(typeParamA));
var methodB =
makeEmptyMethod(returnType: new TypeParameterType(typeParamB));
var methodC =
makeEmptyMethod(returnType: new TypeParameterType(typeParamC));
var a = makeClass(
name: 'A', typeParameters: [typeParamA], procedures: [methodA]);
var b = makeClass(
name: 'B', typeParameters: [typeParamB], procedures: [methodB]);
var c = makeClass(
name: 'C', typeParameters: [typeParamC], procedures: [methodC]);
var d = makeClass(
name: 'D',
supertype: new Supertype(a, [objectType]),
implementedTypes: [
new Supertype(b, [intType]),
new Supertype(c, [numType])
]);
var node = getForwardingNode(d, false);
var stub = node.finalize();
expect(stub.forwardingStubInterfaceTarget, same(methodB));
expect(getStubTarget(stub), isNull);
expect(stub.function.returnType, intType);
}
}