| // 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_program.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 Program program; |
| |
| final CoreTypes coreTypes; |
| |
| ClassHierarchy cachedClassHierarchy; |
| |
| TypeSchemaEnvironment cachedTypeEnvironment; |
| |
| InterfaceResolver cachedInterfaceResolver; |
| |
| InterfaceResolverTest() |
| : this._(new Library(Uri.parse('org-dartlang:///test.dart'), name: 'lib'), |
| createMockSdkProgram()); |
| |
| InterfaceResolverTest._(this.testLib, Program program) |
| : program = program..libraries.add(testLib..parent = program), |
| coreTypes = new CoreTypes(program); |
| |
| ClassHierarchy get classHierarchy { |
| return cachedClassHierarchy ??= new ClassHierarchy(program); |
| } |
| |
| TypeSchemaEnvironment get typeEnvironment { |
| return cachedTypeEnvironment ??= |
| new TypeSchemaEnvironment(coreTypes, classHierarchy, true); |
| } |
| |
| InterfaceResolver get interfaceResolver { |
| return cachedInterfaceResolver ??= |
| new InterfaceResolver(null, typeEnvironment, null, true); |
| } |
| |
| 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(program)); |
| } |
| |
| 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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('index', 0, type: intType), |
| new ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('x', 0, type: new TypeParameterType(T)); |
| var y = |
| new ShadowVariableDeclaration('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 ShadowVariableDeclaration('x', 0, type: new TypeParameterType(T)); |
| var y = |
| new ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('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 ShadowVariableDeclaration('x', 0, |
| type: new TypeParameterType(typeParameter)); |
| var y = new ShadowVariableDeclaration('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(u.isGenericCovariantInterface, isTrue); |
| expect(x.isGenericCovariantImpl, isTrue); |
| expect(x.isGenericCovariantInterface, isTrue); |
| expect(x.isCovariant, isFalse); |
| expect(y.isGenericCovariantImpl, isTrue); |
| expect(y.isGenericCovariantInterface, 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.isGenericCovariantInterface, 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.isGenericCovariantInterface, isFalse); |
| expect(fieldB.isCovariant, isTrue); |
| } |
| |
| void test_field_isGenericCovariantImpl_inherited() { |
| var typeParameter = new TypeParameter('T', objectType); |
| var fieldA = makeField(type: new TypeParameterType(typeParameter)) |
| ..isGenericCovariantInterface = true |
| ..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.isGenericCovariantInterface, isFalse); |
| 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 ShadowVariableDeclaration('x', 0, type: numType) |
| ], namedParameters: [ |
| new ShadowVariableDeclaration('y', 0, type: numType) |
| ]); |
| var methodB = makeEmptyMethod(positionalParameters: [ |
| new ShadowVariableDeclaration('x', 0, type: intType)..isCovariant = true |
| ], namedParameters: [ |
| new ShadowVariableDeclaration('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.isGenericCovariantInterface, isFalse); |
| expect(x.isCovariant, isTrue); |
| var y = stub.function.namedParameters[0]; |
| expect(y.isGenericCovariantImpl, isFalse); |
| expect(y.isGenericCovariantInterface, 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 ShadowVariableDeclaration('x', 0, type: numType) |
| ], namedParameters: [ |
| new ShadowVariableDeclaration('y', 0, type: numType) |
| ]); |
| var typeParameterB = new TypeParameter('T', objectType); |
| var methodB = makeEmptyMethod(typeParameters: [ |
| new TypeParameter('U', new TypeParameterType(typeParameterB)) |
| ..isGenericCovariantImpl = true |
| ..isGenericCovariantInterface = true |
| ], positionalParameters: [ |
| new ShadowVariableDeclaration('x', 0, |
| type: new TypeParameterType(typeParameterB)) |
| ..isGenericCovariantImpl = true |
| ..isGenericCovariantInterface = true |
| ], namedParameters: [ |
| new ShadowVariableDeclaration('y', 0, |
| type: new TypeParameterType(typeParameterB)) |
| ..isGenericCovariantImpl = true |
| ..isGenericCovariantInterface = 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); |
| expect(u.isGenericCovariantInterface, isFalse); |
| var x = stub.function.positionalParameters[0]; |
| expect(x.isGenericCovariantImpl, isTrue); |
| expect(x.isGenericCovariantInterface, isFalse); |
| expect(x.isCovariant, isFalse); |
| var y = stub.function.namedParameters[0]; |
| expect(y.isGenericCovariantImpl, isTrue); |
| expect(y.isGenericCovariantInterface, isFalse); |
| 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 ShadowVariableDeclaration('x', 0, |
| type: objectType, isCovariant: true); |
| var methodA = makeEmptyMethod(positionalParameters: [parameterA]); |
| var parameterB = |
| new ShadowVariableDeclaration('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 ShadowVariableDeclaration('x', 0, type: numType) |
| ]); |
| var typeParamB = new TypeParameter('T', objectType); |
| var methodB = makeEmptyMethod(positionalParameters: [ |
| new ShadowVariableDeclaration('x', 0, |
| type: new TypeParameterType(typeParamB)) |
| ..isGenericCovariantInterface = true |
| ..isGenericCovariantImpl = true |
| ]); |
| var methodC = makeEmptyMethod(positionalParameters: [ |
| new ShadowVariableDeclaration('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); |
| } |
| } |