blob: 806e62285fd2509873e7334664246ba4ba7f0a53 [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.md file.
import 'package:front_end/src/fasta/problems.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
/// A [ForwardingNode] represents a method, getter, or setter within a class's
/// interface that is either implemented in the class directly or inherited from
/// a superclass.
///
/// This class allows us to defer the determination of exactly which member is
/// inherited, as well as the propagation of covariance annotations, and
/// the creation of forwarding stubs, until type inference.
class ForwardingNode extends Procedure {
/// The [InterfaceResolver] that created this [ForwardingNode].
final InterfaceResolver _interfaceResolver;
/// A list containing the directly implemented and directly inherited
/// procedures of the class in question.
///
/// Note that many [ForwardingNode]s share the same [_candidates] list;
/// consult [_start] and [_end] to see which entries in this list are relevant
/// to this [ForwardingNode].
final List<Procedure> _candidates;
/// Indicates whether this forwarding node is for a setter.
final bool _setter;
/// Index of the first entry in [_candidates] relevant to this
/// [ForwardingNode].
final int _start;
/// Index just beyond the last entry in [_candidates] relevant to this
/// [ForwardingNode].
final int _end;
/// The member this node resolves to (if it has been computed); otherwise
/// `null`.
Member _resolution;
ForwardingNode(this._interfaceResolver, Class class_, Name name,
this._candidates, this._setter, this._start, this._end, this._resolution)
: super(name, null, null) {
parent = class_;
}
/// Returns the inherited member, or the forwarding stub, which this node
/// resolves to.
Member resolve() => _resolution ??= _resolve();
/// Determines which inherited member this node resolves to.
Member _resolve() {
// If the class contains a declaration of the member, the resolution is set
// in the constructor call, so we don't have to deal with that case; we only
// have to deal with the case where the interface member is inherited from
// a base class.
//
// If there are multiple inheritance candidates, the inherited member is the
// member whose type is a subtype of all the others. We can find it by two
// passes over the list of members. For the first pass, we step through the
// candidates, updating bestSoFar each time we find a member whose type is a
// subtype of the previous bestSoFar. As we do this, we also work out the
// necessary substitution for matching up type parameters between this class
// and the corresponding superclass.
var bestSoFar = _candidates[_start];
var bestSubstitutionSoFar = _substitutionFor(bestSoFar);
var bestTypeSoFar = bestSubstitutionSoFar
.substituteType(_setter ? bestSoFar.setterType : bestSoFar.getterType);
for (int i = _start + 1; i < _end; i++) {
var candidate = _candidates[i];
var substitution = _substitutionFor(candidate);
bool isBetter;
DartType type;
if (_setter) {
type = substitution.substituteType(candidate.setterType);
// Setters are contravariant in their setter type, so we have to reverse
// the check.
isBetter = _interfaceResolver._typeEnvironment
.isSubtypeOf(bestTypeSoFar, type);
} else {
type = substitution.substituteType(candidate.getterType);
isBetter = _interfaceResolver._typeEnvironment
.isSubtypeOf(type, bestTypeSoFar);
}
if (isBetter) {
bestSoFar = candidate;
bestSubstitutionSoFar = substitution;
bestTypeSoFar = type;
}
}
// For the second pass, we verify that bestSoFar is a subtype of all the
// other potentially inherited members.
// TODO(paulberry): implement this.
// TODO(paulberry): now decide whether we need a forwarding stub or not.
if (bestSoFar is SyntheticAccessor) {
return bestSoFar._field;
} else {
return bestSoFar;
}
}
/// Determines the appropriate substitution to translate type parameters
/// mentioned in the given [candidate] to type parameters on the parent class.
Substitution _substitutionFor(Procedure candidate) {
return Substitution.fromInterfaceType(
_interfaceResolver._typeEnvironment.hierarchy.getTypeAsInstanceOf(
enclosingClass.thisType, candidate.enclosingClass));
}
/// For testing: get the list of candidates relevant to a given node.
static List<Procedure> getCandidates(ForwardingNode node) {
return node._candidates.sublist(node._start, node._end);
}
}
/// An [InterfaceResolver] keeps track of the information necessary to resolve
/// method calls, gets, and sets within a chunk of code being compiled, to
/// infer covariance annotations, and to create forwarwding stubs when necessary
/// to meet covariance requirements.
class InterfaceResolver {
final TypeEnvironment _typeEnvironment;
InterfaceResolver(this._typeEnvironment);
/// Populates [forwardingNodes] with a list of the implemented and inherited
/// members of the given [class_]'s interface.
///
/// Each member of the class's interface is represented by a [ForwardingNode]
/// object.
///
/// If [setters] is `true`, the list will be populated by setters; otherwise
/// it will be populated by getters and methods.
void createForwardingNodes(
Class class_, List<ForwardingNode> forwardingNodes, bool setters) {
// First create a list of candidates for inheritance based on the members
// declared directly in the class.
List<Procedure> candidates = _typeEnvironment.hierarchy
.getDeclaredMembers(class_, setters: setters)
.map((member) => _makeCandidate(member, setters))
.toList();
// Merge in candidates from superclasses.
if (class_.superclass != null) {
candidates = _mergeCandidates(candidates, class_.superclass, setters);
}
for (var supertype in class_.implementedTypes) {
candidates = _mergeCandidates(candidates, supertype.classNode, setters);
}
// Now create a forwarding node for each unique name.
forwardingNodes.length = candidates.length;
int storeIndex = 0;
int i = 0;
while (i < candidates.length) {
var name = candidates[i].name;
int j = i + 1;
while (j < candidates.length && candidates[j].name == name) {
j++;
}
// If candidates[i] came from this class, then it is the interface member
// and no forwarding stub needs to be generated.
var resolvedMember = identical(candidates[i].enclosingClass, class_)
? candidates[i]
: null;
forwardingNodes[storeIndex++] = new ForwardingNode(
this, class_, name, candidates, setters, i, j, resolvedMember);
i = j;
}
forwardingNodes.length = storeIndex;
}
/// Retrieves a list of the interface members of the given [class_].
///
/// If [setters] is true, setters are retrieved; otherwise getters and methods
/// are retrieved.
List<Member> _getInterfaceMembers(Class class_, bool setters) {
// TODO(paulberry): if class_ is being compiled from source, retrieve its
// forwarding nodes.
return _typeEnvironment.hierarchy
.getInterfaceMembers(class_, setters: setters);
}
/// Transforms [member] into a candidate for interface inheritance.
///
/// Fields are transformed into getters and setters; methods are passed
/// through unchanged.
Procedure _makeCandidate(Member member, bool setter) {
if (member is Procedure) return member;
if (member is Field) {
// TODO(paulberry): ensure that the field type is propagated to the
// getter/setter during type inference.
if (setter) {
var valueParam = new VariableDeclaration('_');
var function = new FunctionNode(null,
positionalParameters: [valueParam], returnType: const VoidType());
return new SyntheticAccessor(
member.name, ProcedureKind.Setter, function, member)
..parent = member.enclosingClass;
} else {
var function = new FunctionNode(null);
return new SyntheticAccessor(
member.name, ProcedureKind.Getter, function, member)
..parent = member.enclosingClass;
}
}
return unhandled('${member.runtimeType}', '_makeCandidate', -1, null);
}
/// Merges together the list of interface inheritance candidates in
/// [candidates] with interface inheritance candidates from superclass
/// [class_].
///
/// Any candidates from [class_] are converted into interface inheritance
/// candidates using [_makeCandidate].
List<Procedure> _mergeCandidates(
List<Procedure> candidates, Class class_, bool setters) {
List<Member> members = _getInterfaceMembers(class_, setters);
if (candidates.isEmpty) {
return members.map((member) => _makeCandidate(member, setters)).toList();
}
if (members.isEmpty) return candidates;
List<Procedure> result = <Procedure>[]..length =
candidates.length + members.length;
int storeIndex = 0;
int i = 0, j = 0;
while (i < candidates.length && j < members.length) {
Procedure candidate = candidates[i];
Member member = members[j];
int compare = ClassHierarchy.compareMembers(candidate, member);
if (compare <= 0) {
result[storeIndex++] = candidate;
++i;
// If the same member occurs in both lists, skip the duplicate.
if (identical(candidate, member)) ++j;
} else {
result[storeIndex++] = _makeCandidate(member, setters);
++j;
}
}
while (i < candidates.length) {
result[storeIndex++] = candidates[i++];
}
while (j < members.length) {
result[storeIndex++] = _makeCandidate(members[j++], setters);
}
result.length = storeIndex;
return result;
}
}
/// A [SyntheticAccessor] represents the getter or setter implied by a field.
class SyntheticAccessor extends Procedure {
/// The field associated with the synthetic accessor.
final Field _field;
SyntheticAccessor(
Name name, ProcedureKind kind, FunctionNode function, this._field)
: super(name, kind, function);
}