blob: 1f970f2b5482fda030497628db10b76cad3257e2 [file] [log] [blame]
// Copyright (c) 2013, 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.
part of universe;
class FullFunctionSet extends FunctionSet {
FullFunctionSet(Compiler compiler) : super(compiler);
FunctionSetNode newNode(SourceString name)
=> new FullFunctionSetNode(name);
// TODO(kasperl): This interface is a little bit weird and mostly
// put here to illustrate how we can refine types. Returning a
// selector seems weird because we're only really interested in the
// receiver type information and the type kind.
Selector refine(Selector selector) {
SourceString name = selector.name;
FunctionSetNode node = nodes[name];
if (node == null) return null;
FullFunctionSetQuery query = node.query(selector, compiler);
// If the selector is already exact, then we cannot refine it
// further. If the query isn't exact, it means that the exact
// class didn't contain any mathing element.
if (selector.typeKind == TypedSelectorKind.EXACT) {
if (!query.isExact) return null;
assert(selector.receiverType.element == query.classes[0]);
return selector;
}
// If the query yields an exact class, we refine the selector.
if (query.isExact) {
ClassElement refinement = query.classes[0];
DartType type = refinement.computeType(compiler);
return new TypedSelector.exact(type, selector);
}
// Get the list of classes from the query. If the type information
// cannot be represented in the selector, we avoid refining it.
List<ClassElement> classes = query.classes;
if (classes == null || classes.length != 1) return selector;
if (classes.isEmpty) return null;
// We found one non-exact class, so we try to refine the selector
// to be of subclass kind. We take care to reuse the existing
// selector if matching class.
assert(classes.length == 1);
ClassElement refinement = query.classes[0];
if (selector.typeKind == TypedSelectorKind.SUBCLASS) {
ClassElement existing = selector.receiverType.element;
if (refinement == existing) return selector;
assert(refinement.isSubclassOf(existing));
}
DartType type = refinement.computeType(compiler);
return new TypedSelector.subclass(type, selector);
}
}
class FullFunctionSetNode extends FunctionSetNode {
// To cut down on the time we spend on computing type information
// about the function holders, we limit the number of classes and
// the number of steps it takes to compute them.
static const int MAX_CLASSES = 4;
static const int MAX_CLASSES_STEPS = 32;
FullFunctionSetNode(SourceString name) : super(name);
FunctionSetQuery newQuery(List<Element> functions,
Selector selector,
Compiler compiler) {
List<ClassElement> classes = computeClasses(functions, compiler);
bool isExact = (selector.typeKind == TypedSelectorKind.EXACT)
|| isExactClass(classes, compiler);
return new FullFunctionSetQuery(functions, classes, isExact);
}
static List<ClassElement> computeClasses(List<Element> functions,
Compiler compiler) {
// TODO(kasperl): Check if any of the found classes may have a
// non-throwing noSuchMethod implementation in a subclass instead
// of always disabling the class list computation.
if (compiler.enabledNoSuchMethod) return null;
List<ClassElement> classes = <ClassElement>[];
int budget = MAX_CLASSES_STEPS;
L: for (Element element in functions) {
ClassElement enclosing = element.getEnclosingClass();
for (int i = 0; i < classes.length; i++) {
if (--budget <= 0) {
return null;
} else if (enclosing.isSubclassOf(classes[i])) {
continue L;
} else if (classes[i].isSubclassOf(enclosing)) {
classes[i] = enclosing;
continue L;
}
}
if (classes.length >= MAX_CLASSES) return null;
classes.add(enclosing);
}
return classes;
}
static bool isExactClass(List<ClassElement> classes, Compiler compiler) {
if (classes == null || classes.length != 1) return false;
ClassElement single = classes[0];
// Return true if the single class in our list does not have a
// single instantiated subclass.
Set<ClassElement> subtypes = compiler.world.subtypes[single];
return subtypes == null
|| subtypes.every((ClassElement each) => !each.isSubclassOf(single));
}
}
class FullFunctionSetQuery extends FunctionSetQuery {
final List<ClassElement> classes;
final bool isExact;
FullFunctionSetQuery(List<Element> functions, this.classes, this.isExact)
: super(functions);
}