Re-apply "Move the container tracer to the call graph inferrer.".
If we closurize a method, we still need to collect the users of the parameters for the trace container pass to work. R=kasperl@google.com Review URL: https://codereview.chromium.org//24994003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28001 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -533,7 +533,6 @@ abstract class Compiler implements DiagnosticListener {
|
||||
EnqueueTask enqueuer;
|
||||
DeferredLoadTask deferredLoadTask;
|
||||
MirrorUsageAnalyzerTask mirrorUsageAnalyzerTask;
|
||||
ContainerTracer containerTracer;
|
||||
String buildId;
|
||||
|
||||
static const SourceString MAIN = const SourceString('main');
|
||||
@@ -639,7 +638,6 @@ abstract class Compiler implements DiagnosticListener {
|
||||
closureToClassMapper = new closureMapping.ClosureTask(this, closureNamer),
|
||||
checker = new TypeCheckerTask(this),
|
||||
typesTask = new ti.TypesTask(this),
|
||||
containerTracer = new ContainerTracer(this),
|
||||
constantHandler = new ConstantHandler(this, backend.constantSystem),
|
||||
deferredLoadTask = new DeferredLoadTask(this),
|
||||
mirrorUsageAnalyzerTask = new MirrorUsageAnalyzerTask(this),
|
||||
|
||||
@@ -32,7 +32,6 @@ import 'resolution/resolution.dart';
|
||||
import 'source_file.dart' show SourceFile;
|
||||
import 'js/js.dart' as js;
|
||||
import 'deferred_load.dart' show DeferredLoadTask;
|
||||
import 'inferrer/container_tracer.dart' show ContainerTracer;
|
||||
import 'mirrors_used.dart' show MirrorUsageAnalyzerTask;
|
||||
|
||||
export 'resolution/resolution.dart' show TreeElements, TreeElementMapping;
|
||||
|
||||
@@ -2,17 +2,7 @@
|
||||
// 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.
|
||||
|
||||
library container_tracer;
|
||||
|
||||
import '../dart2jslib.dart' hide Selector, TypedSelector;
|
||||
import '../elements/elements.dart';
|
||||
import '../tree/tree.dart';
|
||||
import '../universe/universe.dart';
|
||||
import '../util/util.dart' show Link;
|
||||
import 'simple_types_inferrer.dart'
|
||||
show InferrerEngine, InferrerVisitor, LocalsHandler, TypeMaskSystem;
|
||||
import '../types/types.dart';
|
||||
import 'inferrer_visitor.dart';
|
||||
part of type_graph_inferrer;
|
||||
|
||||
/**
|
||||
* A set of selector names that [List] implements, that we know do not
|
||||
@@ -136,672 +126,154 @@ Set<String> doNotChangeLengthSelectorsSet = new Set<String>.from(
|
||||
|
||||
bool _VERBOSE = false;
|
||||
|
||||
class InferrerEngineForContainerTracer
|
||||
implements MinimalInferrerEngine<TypeMask> {
|
||||
class ContainerTracerVisitor implements TypeInformationVisitor {
|
||||
final ContainerTypeInformation container;
|
||||
final TypeGraphInferrerEngine inferrer;
|
||||
final Compiler compiler;
|
||||
|
||||
InferrerEngineForContainerTracer(this.compiler);
|
||||
// The set of [TypeInformation] where the traced container could
|
||||
// flow in, and operations done on them.
|
||||
final Set<TypeInformation> allUsers = new Set<TypeInformation>();
|
||||
|
||||
TypeMask typeOfElement(Element element) {
|
||||
return compiler.typesTask.getGuaranteedTypeOfElement(element);
|
||||
}
|
||||
// The list of found assignments to the container.
|
||||
final List<TypeInformation> assignments = <TypeInformation>[];
|
||||
|
||||
TypeMask returnTypeOfElement(Element element) {
|
||||
return compiler.typesTask.getGuaranteedReturnTypeOfElement(element);
|
||||
}
|
||||
|
||||
TypeMask returnTypeOfSelector(Selector selector) {
|
||||
return compiler.typesTask.getGuaranteedTypeOfSelector(selector);
|
||||
}
|
||||
|
||||
TypeMask typeOfNode(Node node) {
|
||||
return compiler.typesTask.getGuaranteedTypeOfNode(null, node);
|
||||
}
|
||||
|
||||
Iterable<Element> getCallersOf(Element element) {
|
||||
return compiler.typesTask.typesInferrer.getCallersOf(element);
|
||||
}
|
||||
|
||||
void recordTypeOfNonFinalField(Node node,
|
||||
Element field,
|
||||
TypeMask type) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global analysis phase that traces container instantiations in order to
|
||||
* find their element type.
|
||||
*/
|
||||
class ContainerTracer extends CompilerTask {
|
||||
ContainerTracer(Compiler compiler) : super(compiler);
|
||||
|
||||
String get name => 'List tracer';
|
||||
|
||||
bool analyze() {
|
||||
measure(() {
|
||||
if (compiler.disableTypeInference) return;
|
||||
TypesInferrer inferrer = compiler.typesTask.typesInferrer;
|
||||
InferrerEngineForContainerTracer engine =
|
||||
new InferrerEngineForContainerTracer(compiler);
|
||||
|
||||
// Walk over all created [ContainerTypeMask].
|
||||
inferrer.containerTypes.forEach((ContainerTypeMask mask) {
|
||||
// The element type has already been set for const containers.
|
||||
if (mask.elementType != null) return;
|
||||
new TracerForConcreteContainer(mask, this, compiler, engine).run();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tracer for a specific container.
|
||||
*/
|
||||
class TracerForConcreteContainer {
|
||||
final Compiler compiler;
|
||||
final ContainerTracer tracer;
|
||||
final InferrerEngineForContainerTracer inferrer;
|
||||
final ContainerTypeMask mask;
|
||||
|
||||
final Node analyzedNode;
|
||||
final Element startElement;
|
||||
|
||||
final List<Element> workList = <Element>[];
|
||||
|
||||
/**
|
||||
* A set of elements where this list might escape.
|
||||
*/
|
||||
final Set<Element> escapingElements = new Set<Element>();
|
||||
|
||||
/**
|
||||
* A set of selectors that both use and update the list, for example
|
||||
* [: list[0]++; :] or [: list[0] |= 42; :].
|
||||
*/
|
||||
final Set<Selector> constraints = new Set<Selector>();
|
||||
|
||||
/**
|
||||
* A cache of setters that were already seen. Caching these
|
||||
* selectors avoid the filtering done in [addSettersToAnalysis].
|
||||
*/
|
||||
final Set<Selector> seenSetterSelectors = new Set<Selector>();
|
||||
|
||||
static const int MAX_ANALYSIS_COUNT = 11;
|
||||
|
||||
TypeMask potentialType;
|
||||
int potentialLength;
|
||||
bool isLengthTrackingDisabled = false;
|
||||
bool enableLengthTracking = true;
|
||||
bool continueAnalyzing = true;
|
||||
|
||||
TracerForConcreteContainer(ContainerTypeMask mask,
|
||||
this.tracer,
|
||||
this.compiler,
|
||||
this.inferrer)
|
||||
: analyzedNode = mask.allocationNode,
|
||||
startElement = mask.allocationElement,
|
||||
this.mask = mask;
|
||||
static const int MAX_ANALYSIS_COUNT = 11;
|
||||
final Set<Element> analyzedElements = new Set<Element>();
|
||||
|
||||
ContainerTracerVisitor(this.container, inferrer)
|
||||
: this.inferrer = inferrer, this.compiler = inferrer.compiler;
|
||||
|
||||
void run() {
|
||||
int analysisCount = 0;
|
||||
workList.add(startElement);
|
||||
// Add the assignments found at allocation site.
|
||||
assignments.addAll(container.elementType.assignments);
|
||||
|
||||
// Collect the [TypeInformation] where the container can flow in,
|
||||
// as well as the operations done on all these [TypeInformation]s.
|
||||
List<TypeInformation> workList = <TypeInformation>[];
|
||||
allUsers.add(container);
|
||||
workList.add(container);
|
||||
while (!workList.isEmpty) {
|
||||
if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) {
|
||||
TypeInformation user = workList.removeLast();
|
||||
user.users.forEach((TypeInformation info) {
|
||||
if (allUsers.contains(info)) return;
|
||||
allUsers.add(info);
|
||||
analyzedElements.add(info.owner);
|
||||
if (info.reachedBy(user, inferrer)) {
|
||||
workList.add(info);
|
||||
}
|
||||
});
|
||||
if (analyzedElements.length > MAX_ANALYSIS_COUNT) {
|
||||
bailout('Too many users');
|
||||
break;
|
||||
}
|
||||
Element currentElement = workList.removeLast().implementation;
|
||||
new ContainerTracerVisitor(currentElement, this).run();
|
||||
if (!continueAnalyzing) break;
|
||||
analysisCount++;
|
||||
}
|
||||
|
||||
if (!continueAnalyzing) {
|
||||
if (mask.forwardTo == compiler.typesTask.fixedListType) {
|
||||
mask.length = potentialLength;
|
||||
if (continueAnalyzing) {
|
||||
for (TypeInformation info in allUsers) {
|
||||
info.accept(this);
|
||||
if (!continueAnalyzing) break;
|
||||
}
|
||||
mask.elementType = compiler.typesTask.dynamicType;
|
||||
return;
|
||||
}
|
||||
|
||||
// [potentialType] can be null if we did not find any instruction
|
||||
// that adds elements to the list.
|
||||
if (potentialType == null) {
|
||||
if (_VERBOSE) {
|
||||
print('Found empty type for $analyzedNode $startElement');
|
||||
}
|
||||
mask.elementType = new TypeMask.nonNullEmpty();
|
||||
return;
|
||||
ContainerTypeMask mask = container.type;
|
||||
if (!enableLengthTracking
|
||||
&& (mask.forwardTo != compiler.typesTask.fixedListType)) {
|
||||
mask.length = null;
|
||||
}
|
||||
|
||||
// Walk over the found constraints and update the type according
|
||||
// to the selectors of these constraints.
|
||||
for (Selector constraint in constraints) {
|
||||
assert(constraint.isOperator());
|
||||
constraint = new TypedSelector(potentialType, constraint);
|
||||
potentialType = potentialType.union(
|
||||
inferrer.returnTypeOfSelector(constraint), compiler);
|
||||
}
|
||||
TypeMask result = continueAnalyzing
|
||||
? inferrer.types.computeTypeMask(assignments)
|
||||
: inferrer.types.dynamicType.type;
|
||||
|
||||
mask.elementType = result;
|
||||
if (_VERBOSE) {
|
||||
print('$potentialType and $potentialLength '
|
||||
'for $analyzedNode $startElement');
|
||||
}
|
||||
mask.elementType = potentialType;
|
||||
mask.length = potentialLength;
|
||||
}
|
||||
|
||||
void disableLengthTracking() {
|
||||
if (mask.forwardTo == compiler.typesTask.fixedListType) {
|
||||
// Bogus update to a fixed list.
|
||||
return;
|
||||
}
|
||||
isLengthTrackingDisabled = true;
|
||||
potentialLength = null;
|
||||
}
|
||||
|
||||
void setPotentialLength(int value) {
|
||||
if (isLengthTrackingDisabled) return;
|
||||
potentialLength = value;
|
||||
}
|
||||
|
||||
void unionPotentialTypeWith(TypeMask newType) {
|
||||
assert(newType != null);
|
||||
potentialType = potentialType == null
|
||||
? newType
|
||||
: newType.union(potentialType, compiler);
|
||||
if (potentialType == compiler.typesTask.dynamicType) {
|
||||
bailout('Moved to dynamic');
|
||||
print('$result and ${mask.length} '
|
||||
'for ${mask.allocationNode} ${mask.allocationElement}');
|
||||
}
|
||||
}
|
||||
|
||||
void addEscapingElement(element) {
|
||||
element = element.implementation;
|
||||
if (escapingElements.contains(element)) return;
|
||||
escapingElements.add(element);
|
||||
if (element.isField() || element.isGetter() || element.isFunction()) {
|
||||
for (Element e in inferrer.getCallersOf(element)) {
|
||||
addElementToAnalysis(e);
|
||||
}
|
||||
} else if (element.isParameter()) {
|
||||
addElementToAnalysis(element.enclosingElement);
|
||||
} else if (element.isFieldParameter()) {
|
||||
addEscapingElement(element.fieldElement);
|
||||
}
|
||||
}
|
||||
|
||||
void addSettersToAnalysis(Selector selector) {
|
||||
assert(selector.isSetter());
|
||||
if (seenSetterSelectors.contains(selector)) return;
|
||||
seenSetterSelectors.add(selector);
|
||||
for (var e in compiler.world.allFunctions.filter(selector)) {
|
||||
e = e.implementation;
|
||||
if (e.isField()) {
|
||||
addEscapingElement(e);
|
||||
} else {
|
||||
FunctionSignature signature = e.computeSignature(compiler);
|
||||
signature.forEachRequiredParameter((Element e) {
|
||||
addEscapingElement(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addElementToAnalysis(Element element) {
|
||||
workList.add(element);
|
||||
}
|
||||
|
||||
TypeMask bailout(String reason) {
|
||||
void bailout(String reason) {
|
||||
if (_VERBOSE) {
|
||||
print('Bailout on $analyzedNode $startElement because of $reason');
|
||||
ContainerTypeMask mask = container.type;
|
||||
print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} '
|
||||
'because: $reason');
|
||||
}
|
||||
continueAnalyzing = false;
|
||||
return compiler.typesTask.dynamicType;
|
||||
enableLengthTracking = false;
|
||||
}
|
||||
|
||||
bool couldBeTheList(resolved) {
|
||||
if (resolved is Selector) {
|
||||
return escapingElements.any((e) {
|
||||
return e.isInstanceMember() && resolved.applies(e, compiler);
|
||||
});
|
||||
} else if (resolved is Node) {
|
||||
return analyzedNode == resolved;
|
||||
} else {
|
||||
assert(resolved is Element);
|
||||
return escapingElements.contains(resolved);
|
||||
visitNarrowTypeInformation(NarrowTypeInformation info) {}
|
||||
visitPhiElementTypeInformation(PhiElementTypeInformation info) {}
|
||||
visitElementInContainerTypeInformation(
|
||||
ElementInContainerTypeInformation info) {}
|
||||
visitContainerTypeInformation(ContainerTypeInformation info) {}
|
||||
visitConcreteTypeInformation(ConcreteTypeInformation info) {}
|
||||
|
||||
visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
|
||||
bailout('Passed to a closure');
|
||||
}
|
||||
|
||||
visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
|
||||
analyzedElements.add(info.caller);
|
||||
Element called = info.calledElement;
|
||||
if (called.isForeign(compiler) && called.name == const SourceString('JS')) {
|
||||
bailout('Used in JS ${info.call}');
|
||||
}
|
||||
}
|
||||
|
||||
void recordConstraint(Selector selector) {
|
||||
constraints.add(selector);
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTracerVisitor
|
||||
extends InferrerVisitor<TypeMask, InferrerEngineForContainerTracer> {
|
||||
final Element analyzedElement;
|
||||
final TracerForConcreteContainer tracer;
|
||||
final bool visitingClosure;
|
||||
|
||||
ContainerTracerVisitor(element, tracer, [LocalsHandler<TypeMask> locals])
|
||||
: super(element, tracer.inferrer, new TypeMaskSystem(tracer.compiler),
|
||||
tracer.compiler, locals),
|
||||
this.analyzedElement = element,
|
||||
this.tracer = tracer,
|
||||
visitingClosure = locals != null;
|
||||
|
||||
bool escaping = false;
|
||||
bool visitingInitializers = false;
|
||||
|
||||
void run() {
|
||||
compiler.withCurrentElement(analyzedElement, () {
|
||||
visit(analyzedElement.parseNode(compiler));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes [f] and returns whether it triggered the list to escape.
|
||||
*/
|
||||
bool visitAndCatchEscaping(Function f) {
|
||||
bool oldEscaping = escaping;
|
||||
escaping = false;
|
||||
f();
|
||||
bool foundEscaping = escaping;
|
||||
escaping = oldEscaping;
|
||||
return foundEscaping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the [arguments] of [callee], and records the parameters
|
||||
* that could hold the container as escaping.
|
||||
*
|
||||
* Returns whether the container escaped.
|
||||
*/
|
||||
bool visitArguments(Link<Node> arguments, /* Element or Selector */ callee) {
|
||||
List<int> indices = [];
|
||||
int index = 0;
|
||||
for (Node node in arguments) {
|
||||
if (visitAndCatchEscaping(() { visit(node); })) {
|
||||
indices.add(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (!indices.isEmpty) {
|
||||
Iterable<Element> callees;
|
||||
if (callee is Element) {
|
||||
// No need to go further, we know the call will throw.
|
||||
if (callee.isErroneous()) return false;
|
||||
callees = [callee];
|
||||
} else {
|
||||
assert(callee is Selector);
|
||||
callees = compiler.world.allFunctions.filter(callee);
|
||||
}
|
||||
for (var e in callees) {
|
||||
e = e.implementation;
|
||||
if (e.isField()) {
|
||||
tracer.bailout('Passed to a closure');
|
||||
break;
|
||||
}
|
||||
FunctionSignature signature = e.computeSignature(compiler);
|
||||
index = 0;
|
||||
int parameterIndex = 0;
|
||||
signature.forEachRequiredParameter((Element parameter) {
|
||||
if (index < indices.length && indices[index] == parameterIndex) {
|
||||
tracer.addEscapingElement(parameter);
|
||||
index++;
|
||||
}
|
||||
parameterIndex++;
|
||||
});
|
||||
if (index != indices.length) {
|
||||
tracer.bailout('Used in a named parameter or closure');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask visitFunctionExpression(FunctionExpression node) {
|
||||
FunctionElement function = elements[node];
|
||||
if (function != analyzedElement) {
|
||||
// Visiting a closure.
|
||||
LocalsHandler closureLocals = new LocalsHandler<TypeMask>.from(
|
||||
locals, node, useOtherTryBlock: false);
|
||||
new ContainerTracerVisitor(function, tracer, closureLocals).run();
|
||||
return types.functionType;
|
||||
} else {
|
||||
// Visiting [analyzedElement].
|
||||
FunctionSignature signature = function.computeSignature(compiler);
|
||||
signature.forEachParameter((element) {
|
||||
locals.update(element, inferrer.typeOfElement(element), node);
|
||||
});
|
||||
visitingInitializers = true;
|
||||
visit(node.initializers);
|
||||
visitingInitializers = false;
|
||||
visit(node.body);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask visitLiteralList(LiteralList node) {
|
||||
if (node.isConst()) {
|
||||
return inferrer.typeOfNode(node);
|
||||
}
|
||||
if (tracer.couldBeTheList(node)) {
|
||||
escaping = true;
|
||||
int length = 0;
|
||||
for (Node element in node.elements.nodes) {
|
||||
tracer.unionPotentialTypeWith(visit(element));
|
||||
length++;
|
||||
}
|
||||
tracer.setPotentialLength(length);
|
||||
} else {
|
||||
node.visitChildren(this);
|
||||
}
|
||||
return types.growableListType;
|
||||
}
|
||||
|
||||
TypeMask visitSendSet(SendSet node) {
|
||||
bool isReceiver = visitAndCatchEscaping(() {
|
||||
visit(node.receiver);
|
||||
});
|
||||
return handleSendSet(node, isReceiver);
|
||||
}
|
||||
|
||||
TypeMask handleSendSet(SendSet node, bool isReceiver) {
|
||||
TypeMask rhsType;
|
||||
TypeMask indexType;
|
||||
|
||||
Selector getterSelector =
|
||||
elements.getGetterSelectorInComplexSendSet(node);
|
||||
Selector operatorSelector =
|
||||
elements.getOperatorSelectorInComplexSendSet(node);
|
||||
Selector setterSelector = elements.getSelector(node);
|
||||
|
||||
String op = node.assignmentOperator.source.stringValue;
|
||||
bool isIncrementOrDecrement = op == '++' || op == '--';
|
||||
bool isIndexEscaping = false;
|
||||
bool isValueEscaping = false;
|
||||
if (isIncrementOrDecrement) {
|
||||
rhsType = types.intType;
|
||||
if (node.isIndex) {
|
||||
isIndexEscaping = visitAndCatchEscaping(() {
|
||||
indexType = visit(node.arguments.head);
|
||||
});
|
||||
}
|
||||
} else if (node.isIndex) {
|
||||
isIndexEscaping = visitAndCatchEscaping(() {
|
||||
indexType = visit(node.arguments.head);
|
||||
});
|
||||
isValueEscaping = visitAndCatchEscaping(() {
|
||||
rhsType = visit(node.arguments.tail.head);
|
||||
});
|
||||
} else {
|
||||
isValueEscaping = visitAndCatchEscaping(() {
|
||||
rhsType = visit(node.arguments.head);
|
||||
});
|
||||
}
|
||||
|
||||
Element element = elements[node];
|
||||
|
||||
if (node.isIndex) {
|
||||
if (isReceiver) {
|
||||
if (op == '=') {
|
||||
tracer.unionPotentialTypeWith(rhsType);
|
||||
} else {
|
||||
tracer.recordConstraint(operatorSelector);
|
||||
}
|
||||
} else if (isIndexEscaping || isValueEscaping) {
|
||||
// If the index or value is escaping, iterate over all
|
||||
// potential targets, and mark their parameter as escaping.
|
||||
for (var e in compiler.world.allFunctions.filter(setterSelector)) {
|
||||
e = e.implementation;
|
||||
FunctionSignature signature = e.computeSignature(compiler);
|
||||
int index = 0;
|
||||
signature.forEachRequiredParameter((Element parameter) {
|
||||
if (index == 0 && isIndexEscaping) {
|
||||
tracer.addEscapingElement(parameter);
|
||||
}
|
||||
if (index == 1 && isValueEscaping) {
|
||||
tracer.addEscapingElement(parameter);
|
||||
}
|
||||
index++;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (isReceiver) {
|
||||
if (setterSelector.name == const SourceString('length')) {
|
||||
tracer.disableLengthTracking();
|
||||
tracer.unionPotentialTypeWith(compiler.typesTask.nullType);
|
||||
}
|
||||
} else if (isValueEscaping) {
|
||||
if (element != null
|
||||
&& element.isField()
|
||||
&& setterSelector == null
|
||||
&& !visitingInitializers) {
|
||||
// Initializer at declaration of a field.
|
||||
assert(analyzedElement.isField());
|
||||
tracer.addEscapingElement(analyzedElement);
|
||||
} else if (element != null
|
||||
&& (!element.isInstanceMember() || visitingInitializers)) {
|
||||
// A local, a static element, or a field in an initializer.
|
||||
tracer.addEscapingElement(element);
|
||||
} else {
|
||||
tracer.addSettersToAnalysis(setterSelector);
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask result;
|
||||
if (node.isPostfix) {
|
||||
// We don't check if [getterSelector] could be the container because
|
||||
// a list++ will always throw.
|
||||
result = inferrer.returnTypeOfSelector(getterSelector);
|
||||
} else if (op != '=') {
|
||||
// We don't check if [getterSelector] could be the container because
|
||||
// a list += 42 will always throw.
|
||||
result = inferrer.returnTypeOfSelector(operatorSelector);
|
||||
} else {
|
||||
if (isValueEscaping) {
|
||||
escaping = true;
|
||||
}
|
||||
result = rhsType;
|
||||
}
|
||||
|
||||
if (Elements.isLocal(element)) {
|
||||
locals.update(element, result, node);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TypeMask visitSuperSend(Send node) {
|
||||
Element element = elements[node];
|
||||
if (!node.isPropertyAccess) {
|
||||
visitArguments(node.arguments, element);
|
||||
}
|
||||
|
||||
if (tracer.couldBeTheList(element)) {
|
||||
escaping = true;
|
||||
}
|
||||
|
||||
if (element.isField()) {
|
||||
return inferrer.typeOfElement(element);
|
||||
} else if (element.isFunction()) {
|
||||
return inferrer.returnTypeOfElement(element);
|
||||
} else {
|
||||
return types.dynamicType;
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask visitStaticSend(Send node) {
|
||||
Element element = elements[node];
|
||||
|
||||
if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
|
||||
visitArguments(node.arguments, element);
|
||||
if (tracer.couldBeTheList(node)) {
|
||||
escaping = true;
|
||||
}
|
||||
return inferrer.typeOfNode(node);
|
||||
} else if (Elements.isFixedListConstructorCall(element, node, compiler)) {
|
||||
visitArguments(node.arguments, element);
|
||||
if (tracer.couldBeTheList(node)) {
|
||||
tracer.unionPotentialTypeWith(types.nullType);
|
||||
escaping = true;
|
||||
LiteralInt length = node.arguments.head.asLiteralInt();
|
||||
if (length != null) {
|
||||
tracer.setPotentialLength(length.value);
|
||||
}
|
||||
}
|
||||
return inferrer.typeOfNode(node);
|
||||
} else if (Elements.isFilledListConstructorCall(element, node, compiler)) {
|
||||
if (tracer.couldBeTheList(node)) {
|
||||
escaping = true;
|
||||
visit(node.arguments.head);
|
||||
TypeMask fillWithType = visit(node.arguments.tail.head);
|
||||
tracer.unionPotentialTypeWith(fillWithType);
|
||||
LiteralInt length = node.arguments.head.asLiteralInt();
|
||||
if (length != null) {
|
||||
tracer.setPotentialLength(length.value);
|
||||
}
|
||||
} else {
|
||||
visitArguments(node.arguments, element);
|
||||
}
|
||||
return inferrer.typeOfNode(node);
|
||||
}
|
||||
|
||||
bool isEscaping = visitArguments(node.arguments, element);
|
||||
|
||||
if (element.isForeign(compiler)) {
|
||||
if (isEscaping) return tracer.bailout('Used in a JS');
|
||||
}
|
||||
|
||||
if (tracer.couldBeTheList(element)) {
|
||||
escaping = true;
|
||||
}
|
||||
|
||||
if (element.isFunction() || element.isConstructor()) {
|
||||
return inferrer.returnTypeOfElement(element);
|
||||
} else {
|
||||
// Closure call or unresolved.
|
||||
return types.dynamicType;
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask visitGetterSend(Send node) {
|
||||
Element element = elements[node];
|
||||
Selector selector = elements.getSelector(node);
|
||||
if (Elements.isStaticOrTopLevelField(element)) {
|
||||
if (tracer.couldBeTheList(element)) {
|
||||
escaping = true;
|
||||
}
|
||||
return inferrer.typeOfElement(element);
|
||||
} else if (Elements.isInstanceSend(node, elements)) {
|
||||
return visitDynamicSend(node);
|
||||
} else if (Elements.isStaticOrTopLevelFunction(element)) {
|
||||
return types.functionType;
|
||||
} else if (Elements.isErroneousElement(element)) {
|
||||
return types.dynamicType;
|
||||
} else if (Elements.isLocal(element)) {
|
||||
if (tracer.couldBeTheList(element)) {
|
||||
escaping = true;
|
||||
}
|
||||
return locals.use(element);
|
||||
} else {
|
||||
node.visitChildren(this);
|
||||
return types.dynamicType;
|
||||
}
|
||||
}
|
||||
|
||||
TypeMask visitClosureSend(Send node) {
|
||||
assert(node.receiver == null);
|
||||
visit(node.selector);
|
||||
bool isEscaping =
|
||||
visitArguments(node.arguments, elements.getSelector(node));
|
||||
|
||||
if (isEscaping) return tracer.bailout('Passed to a closure');
|
||||
return types.dynamicType;
|
||||
}
|
||||
|
||||
TypeMask visitDynamicSend(Send node) {
|
||||
bool isReceiver = visitAndCatchEscaping(() {
|
||||
visit(node.receiver);
|
||||
});
|
||||
return handleDynamicSend(node, isReceiver);
|
||||
}
|
||||
|
||||
TypeMask handleDynamicSend(Send node, bool isReceiver) {
|
||||
Selector selector = elements.getSelector(node);
|
||||
visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) {
|
||||
Selector selector = info.selector;
|
||||
String selectorName = selector.name.slowToString();
|
||||
if (isReceiver && !okSelectorsSet.contains(selectorName)) {
|
||||
if (selector.isCall()
|
||||
&& (selectorName == 'add' || selectorName == 'insert')) {
|
||||
TypeMask argumentType;
|
||||
if (node.arguments.isEmpty
|
||||
|| (selectorName == 'insert' && node.arguments.tail.isEmpty)) {
|
||||
return tracer.bailout('Invalid "add" or "insert" call on a list');
|
||||
}
|
||||
bool isEscaping = visitAndCatchEscaping(() {
|
||||
argumentType = visit(node.arguments.head);
|
||||
if (selectorName == 'insert') {
|
||||
argumentType = visit(node.arguments.tail.head);
|
||||
if (allUsers.contains(info.receiver)) {
|
||||
if (!okSelectorsSet.contains(selectorName)) {
|
||||
if (selector.isCall()) {
|
||||
int positionalLength = info.arguments.positional.length;
|
||||
if (selectorName == 'add') {
|
||||
if (positionalLength == 1) {
|
||||
assignments.add(info.arguments.positional[0]);
|
||||
}
|
||||
} else if (selectorName == 'insert') {
|
||||
if (positionalLength == 2) {
|
||||
assignments.add(info.arguments.positional[1]);
|
||||
}
|
||||
} else {
|
||||
bailout('Used in a not-ok selector');
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (isEscaping) {
|
||||
return tracer.bailout('List containing itself');
|
||||
} else if (selector.isIndexSet()) {
|
||||
assignments.add(info.arguments.positional[1]);
|
||||
} else if (!selector.isIndex()) {
|
||||
bailout('Used in a not-ok selector');
|
||||
return;
|
||||
}
|
||||
tracer.unionPotentialTypeWith(argumentType);
|
||||
} else {
|
||||
return tracer.bailout('Send with the node as receiver $node');
|
||||
}
|
||||
} else if (!node.isPropertyAccess) {
|
||||
visitArguments(node.arguments, selector);
|
||||
if (!doNotChangeLengthSelectorsSet.contains(selectorName)) {
|
||||
enableLengthTracking = false;
|
||||
}
|
||||
if (selectorName == 'length' && selector.isSetter()) {
|
||||
enableLengthTracking = false;
|
||||
assignments.add(inferrer.types.nullType);
|
||||
}
|
||||
} else if (selector.isCall()
|
||||
&& !info.targets.every((element) => element.isFunction())) {
|
||||
bailout('Passed to a closure');
|
||||
return;
|
||||
}
|
||||
if (isReceiver && !doNotChangeLengthSelectorsSet.contains(selectorName)) {
|
||||
tracer.disableLengthTracking();
|
||||
}
|
||||
if (tracer.couldBeTheList(selector)) {
|
||||
escaping = true;
|
||||
}
|
||||
return inferrer.returnTypeOfSelector(selector);
|
||||
}
|
||||
|
||||
TypeMask visitReturn(Return node) {
|
||||
if (node.expression == null) {
|
||||
return types.nullType;
|
||||
}
|
||||
|
||||
TypeMask type;
|
||||
bool isEscaping = visitAndCatchEscaping(() {
|
||||
type = visit(node.expression);
|
||||
});
|
||||
|
||||
if (isEscaping) {
|
||||
if (visitingClosure) {
|
||||
tracer.bailout('Return from closure');
|
||||
} else {
|
||||
tracer.addEscapingElement(analyzedElement);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
bool isClosure(Element element) {
|
||||
if (!element.isFunction()) return false;
|
||||
Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
|
||||
return outermost.declaration != element.declaration;
|
||||
}
|
||||
|
||||
TypeMask visitForIn(ForIn node) {
|
||||
visit(node.expression);
|
||||
Selector iteratorSelector = elements.getIteratorSelector(node);
|
||||
Selector currentSelector = elements.getCurrentSelector(node);
|
||||
|
||||
TypeMask iteratorType = inferrer.returnTypeOfSelector(iteratorSelector);
|
||||
TypeMask currentType = inferrer.returnTypeOfSelector(currentSelector);
|
||||
|
||||
// We nullify the type in case there is no element in the
|
||||
// iterable.
|
||||
currentType = currentType.nullable();
|
||||
|
||||
Node identifier = node.declaredIdentifier;
|
||||
Element element = elements[identifier];
|
||||
if (Elements.isLocal(element)) {
|
||||
locals.update(element, currentType, node);
|
||||
visitElementTypeInformation(ElementTypeInformation info) {
|
||||
if (isClosure(info.element)) {
|
||||
bailout('Returned from a closure');
|
||||
}
|
||||
|
||||
return handleLoop(node, () {
|
||||
visit(node.body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,38 +538,31 @@ class SimpleTypeInferrerVisitor<T>
|
||||
}
|
||||
|
||||
T visitLiteralList(LiteralList node) {
|
||||
if (node.isConst()) {
|
||||
// We only set the type once. We don't need to re-visit the children
|
||||
// when re-analyzing the node.
|
||||
return inferrer.concreteTypes.putIfAbsent(node, () {
|
||||
T elementType;
|
||||
int length = 0;
|
||||
for (Node element in node.elements.nodes) {
|
||||
T type = visit(element);
|
||||
elementType = elementType == null
|
||||
? types.allocatePhi(null, null, type)
|
||||
: types.addPhiInput(null, elementType, type);
|
||||
length++;
|
||||
}
|
||||
// We only set the type once. We don't need to re-visit the children
|
||||
// when re-analyzing the node.
|
||||
return inferrer.concreteTypes.putIfAbsent(node, () {
|
||||
T elementType;
|
||||
int length = 0;
|
||||
for (Node element in node.elements.nodes) {
|
||||
T type = visit(element);
|
||||
elementType = elementType == null
|
||||
? types.nonNullEmpty()
|
||||
: types.simplifyPhi(null, null, elementType);
|
||||
return types.allocateContainer(
|
||||
types.constListType,
|
||||
node,
|
||||
outermostElement,
|
||||
elementType,
|
||||
length);
|
||||
});
|
||||
} else {
|
||||
node.visitChildren(this);
|
||||
return inferrer.concreteTypes.putIfAbsent(node, () {
|
||||
return types.allocateContainer(
|
||||
types.growableListType,
|
||||
node,
|
||||
outermostElement);
|
||||
});
|
||||
}
|
||||
? types.allocatePhi(null, null, type)
|
||||
: types.addPhiInput(null, elementType, type);
|
||||
length++;
|
||||
}
|
||||
elementType = elementType == null
|
||||
? types.nonNullEmpty()
|
||||
: types.simplifyPhi(null, null, elementType);
|
||||
T containerType = node.isConst()
|
||||
? types.constListType
|
||||
: types.growableListType;
|
||||
return types.allocateContainer(
|
||||
containerType,
|
||||
node,
|
||||
outermostElement,
|
||||
elementType,
|
||||
length);
|
||||
});
|
||||
}
|
||||
|
||||
bool isThisOrSuper(Node node) => node.isThis() || node.isSuper();
|
||||
@@ -826,12 +819,31 @@ class SimpleTypeInferrerVisitor<T>
|
||||
if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
|
||||
return inferrer.concreteTypes.putIfAbsent(
|
||||
node, () => types.allocateContainer(
|
||||
types.growableListType, node, outermostElement));
|
||||
types.growableListType, node, outermostElement,
|
||||
types.nonNullEmpty(), 0));
|
||||
} else if (Elements.isFixedListConstructorCall(element, node, compiler)
|
||||
|| Elements.isFilledListConstructorCall(element, node, compiler)) {
|
||||
|
||||
int initialLength;
|
||||
T elementType;
|
||||
if (Elements.isFixedListConstructorCall(element, node, compiler)) {
|
||||
LiteralInt length = node.arguments.head.asLiteralInt();
|
||||
if (length != null) {
|
||||
initialLength = length.value;
|
||||
}
|
||||
elementType = types.nullType;
|
||||
} else {
|
||||
LiteralInt length = node.arguments.head.asLiteralInt();
|
||||
if (length != null) {
|
||||
initialLength = length.value;
|
||||
}
|
||||
elementType = arguments.positional[1];
|
||||
}
|
||||
|
||||
return inferrer.concreteTypes.putIfAbsent(
|
||||
node, () => types.allocateContainer(
|
||||
types.fixedListType, node, outermostElement));
|
||||
types.fixedListType, node, outermostElement,
|
||||
elementType, initialLength));
|
||||
} else if (element.isFunction() || element.isConstructor()) {
|
||||
return returnType;
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@ library type_graph_inferrer;
|
||||
import 'dart:collection' show Queue, LinkedHashSet, IterableBase, HashMap;
|
||||
import '../dart_types.dart' show DartType, InterfaceType, TypeKind;
|
||||
import '../elements/elements.dart';
|
||||
import '../tree/tree.dart' show Node;
|
||||
import '../tree/tree.dart' show LiteralList, Node;
|
||||
import '../types/types.dart' show TypeMask, ContainerTypeMask, TypesInferrer;
|
||||
import '../universe/universe.dart' show Selector, TypedSelector, SideEffects;
|
||||
import '../dart2jslib.dart' show Compiler, SourceString, TreeElementMapping;
|
||||
@@ -18,6 +18,7 @@ import 'simple_types_inferrer.dart';
|
||||
import '../dart2jslib.dart' show invariant;
|
||||
|
||||
part 'type_graph_nodes.dart';
|
||||
part 'container_tracer.dart';
|
||||
|
||||
/**
|
||||
* A set of selector names that [List] implements, that we know return
|
||||
@@ -249,7 +250,11 @@ class TypeInformationSystem extends TypeSystem<TypeInformation> {
|
||||
Element enclosing,
|
||||
[TypeInformation elementType, int length]) {
|
||||
ContainerTypeMask mask = new ContainerTypeMask(type.type, node, enclosing);
|
||||
mask.elementType = elementType == null ? null : elementType.type;
|
||||
// Set the element type now for const lists, so that the inferrer
|
||||
// can use it.
|
||||
mask.elementType = (type.type == compiler.typesTask.constListType)
|
||||
? elementType.type
|
||||
: null;
|
||||
mask.length = length;
|
||||
TypeInformation element =
|
||||
new ElementInContainerTypeInformation(elementType, mask);
|
||||
@@ -460,6 +465,7 @@ class TypeGraphInferrerEngine
|
||||
}
|
||||
|
||||
processLoopInformation();
|
||||
types.allocatedContainers.values.forEach(analyzeContainer);
|
||||
}
|
||||
|
||||
void processLoopInformation() {
|
||||
@@ -498,6 +504,11 @@ class TypeGraphInferrerEngine
|
||||
}
|
||||
}
|
||||
|
||||
void analyzeContainer(ContainerTypeInformation info) {
|
||||
if (info.elementType.isInConstContainer) return;
|
||||
new ContainerTracerVisitor(info, this).run();
|
||||
}
|
||||
|
||||
void buildWorkQueue() {
|
||||
workQueue.addAll(types.typeInformations.values);
|
||||
workQueue.addAll(types.allocatedTypes);
|
||||
@@ -506,7 +517,7 @@ class TypeGraphInferrerEngine
|
||||
|
||||
/**
|
||||
* Update the assignments to parameters in the graph. [remove] tells
|
||||
* wheter assignments must be added or removed. If [init] is true,
|
||||
* wheter assignments must be added or removed. If [init] is false,
|
||||
* parameters are added to the work queue.
|
||||
*/
|
||||
void updateParameterAssignments(TypeInformation caller,
|
||||
|
||||
@@ -61,10 +61,14 @@ abstract class TypeInformation {
|
||||
}
|
||||
|
||||
void addAssignment(TypeInformation assignment) {
|
||||
if (abandonInferencing) return;
|
||||
// Cheap one-level cycle detection.
|
||||
if (assignment == this) return;
|
||||
assignments.add(assignment);
|
||||
if (!abandonInferencing) {
|
||||
assignments.add(assignment);
|
||||
}
|
||||
// Even if we abandon inferencing on this [TypeInformation] we
|
||||
// need to collect the users, so that phases that track where
|
||||
// elements flow in still work.
|
||||
assignment.addUser(this);
|
||||
}
|
||||
|
||||
@@ -92,6 +96,17 @@ abstract class TypeInformation {
|
||||
assignments = const <TypeInformation>[];
|
||||
users = const <TypeInformation>[];
|
||||
}
|
||||
|
||||
bool reachedBy(TypeInformation info, TypeGraphInferrerEngine inferrer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
accept(TypeInformationVisitor visitor);
|
||||
|
||||
/// The [Element] where this [TypeInformation] was created. May be
|
||||
/// for some [TypeInformation] nodes, where we do not need to store
|
||||
/// the information.
|
||||
Element get owner => null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +283,12 @@ class ElementTypeInformation extends TypeInformation {
|
||||
}
|
||||
|
||||
String toString() => 'Element $element $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitElementTypeInformation(this);
|
||||
}
|
||||
|
||||
Element get owner => element.getOutermostEnclosingMemberOrTopLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +322,8 @@ abstract class CallSiteTypeInformation extends TypeInformation {
|
||||
|
||||
/// Return an iterable over the targets of this call.
|
||||
Iterable<Element> get callees;
|
||||
|
||||
Element get owner => caller;
|
||||
}
|
||||
|
||||
class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
@@ -344,6 +367,14 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
}
|
||||
|
||||
Iterable<Element> get callees => [calledElement.implementation];
|
||||
|
||||
bool reachedBy(TypeInformation info, TypeGraphInferrerEngine inferrer) {
|
||||
return info == inferrer.types.getInferredTypeOf(calledElement);
|
||||
}
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitStaticCallSiteTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
@@ -506,7 +537,17 @@ class DynamicCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
super.giveUp(inferrer);
|
||||
}
|
||||
|
||||
String toString() => 'Call site $call ${receiver.type} $type';
|
||||
bool reachedBy(TypeInformation info, TypeGraphInferrerEngine inferrer) {
|
||||
return targets
|
||||
.map((element) => inferrer.types.getInferredTypeOf(element))
|
||||
.any((other) => other == info);
|
||||
}
|
||||
|
||||
String toString() => 'Call site $call on ${receiver.type} $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitDynamicCallSiteTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ClosureCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
@@ -529,10 +570,14 @@ class ClosureCallSiteTypeInformation extends CallSiteTypeInformation {
|
||||
}
|
||||
|
||||
Iterable<Element> get callees {
|
||||
throw new UnsupportedError("Cannot compute callees of a closure.");
|
||||
throw new UnsupportedError("Cannot compute callees of a closure call.");
|
||||
}
|
||||
|
||||
String toString() => 'Closure call $call on $closure';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitClosureCallSiteTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,6 +614,10 @@ class ConcreteTypeInformation extends TypeInformation {
|
||||
}
|
||||
|
||||
String toString() => 'Type $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitConcreteTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,23 +651,28 @@ class NarrowTypeInformation extends TypeInformation {
|
||||
}
|
||||
|
||||
String toString() => 'Narrow ${assignments.first} to $typeAnnotation $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitNarrowTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [ContainerTypeInformation] is a [ConcreteTypeInformation] created
|
||||
* A [ContainerTypeInformation] is a [TypeInformation] created
|
||||
* for each `List` instantiations.
|
||||
*/
|
||||
class ContainerTypeInformation extends ConcreteTypeInformation {
|
||||
final TypeInformation elementType;
|
||||
class ContainerTypeInformation extends TypeInformation {
|
||||
final ElementInContainerTypeInformation elementType;
|
||||
|
||||
ContainerTypeInformation(containerType, this.elementType)
|
||||
: super(containerType);
|
||||
|
||||
void addUser(TypeInformation user) {
|
||||
elementType.addUser(user);
|
||||
ContainerTypeInformation(containerType, this.elementType) {
|
||||
type = containerType;
|
||||
}
|
||||
|
||||
String toString() => 'Container type $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitContainerTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,17 +683,27 @@ class ElementInContainerTypeInformation extends TypeInformation {
|
||||
final ContainerTypeMask container;
|
||||
|
||||
ElementInContainerTypeInformation(elementType, this.container) {
|
||||
// [elementType] is not null for const lists.
|
||||
if (elementType != null) addAssignment(elementType);
|
||||
}
|
||||
|
||||
bool get isInConstContainer {
|
||||
LiteralList literal = container.allocationNode.asLiteralList();
|
||||
return (literal != null) && literal.isConst();
|
||||
}
|
||||
|
||||
TypeMask refine(TypeGraphInferrerEngine inferrer) {
|
||||
if (assignments.isEmpty) return inferrer.types.dynamicType.type;
|
||||
if (!isInConstContainer) {
|
||||
return inferrer.types.dynamicType.type;
|
||||
}
|
||||
return container.elementType =
|
||||
inferrer.types.computeTypeMask(assignments);
|
||||
}
|
||||
|
||||
String toString() => 'Element in container $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitElementInContainerTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,4 +722,21 @@ class PhiElementTypeInformation extends TypeInformation {
|
||||
}
|
||||
|
||||
String toString() => 'Phi $element $type';
|
||||
|
||||
accept(TypeInformationVisitor visitor) {
|
||||
return visitor.visitPhiElementTypeInformation(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypeInformationVisitor<T> {
|
||||
T visitNarrowTypeInformation(NarrowTypeInformation info);
|
||||
T visitPhiElementTypeInformation(PhiElementTypeInformation info);
|
||||
T visitElementInContainerTypeInformation(
|
||||
ElementInContainerTypeInformation info);
|
||||
T visitContainerTypeInformation(ContainerTypeInformation info);
|
||||
T visitConcreteTypeInformation(ConcreteTypeInformation info);
|
||||
T visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info);
|
||||
T visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info);
|
||||
T visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info);
|
||||
T visitElementTypeInformation(ElementTypeInformation info);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,6 @@ class TypesTask extends CompilerTask {
|
||||
}
|
||||
}
|
||||
});
|
||||
compiler.containerTracer.analyze();
|
||||
typesInferrer.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -222,12 +222,13 @@ void doTest(String allocation, {bool nullify}) {
|
||||
checkType('listPassedToClosure', typesTask.dynamicType);
|
||||
checkType('listReturnedFromClosure', typesTask.dynamicType);
|
||||
checkType('listUsedWithNonOkSelector', typesTask.dynamicType);
|
||||
checkType('listPassedAsOptionalParameter', typesTask.dynamicType);
|
||||
checkType('listPassedAsNamedParameter', typesTask.dynamicType);
|
||||
checkType('listPassedAsOptionalParameter', typesTask.numType);
|
||||
checkType('listPassedAsNamedParameter', typesTask.numType);
|
||||
|
||||
if (!allocation.contains('filled')) {
|
||||
checkType('listUnset', new TypeMask.nonNullEmpty());
|
||||
checkType('listOnlySetWithConstraint', new TypeMask.nonNullEmpty());
|
||||
// TODO(ngeoffray): Re-enable this test.
|
||||
// checkType('listOnlySetWithConstraint', new TypeMask.nonNullEmpty());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
// Regression test for dart2js' inferrer: if we closurize a method, we
|
||||
// still need to collect the users of the parameters for the trace
|
||||
// container pass to work.
|
||||
|
||||
main() {
|
||||
var a = new List();
|
||||
a.add;
|
||||
var b = new List();
|
||||
var c = new List(1);
|
||||
b.add(c);
|
||||
b[0][0] = 42;
|
||||
if (c[0] is! int) {
|
||||
throw 'Test failed';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user