| Index: sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (revision 27994)
|
| +++ sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (working copy)
|
| @@ -2,8 +2,18 @@
|
| // 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 type_graph_inferrer;
|
| +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';
|
| +
|
| /**
|
| * A set of selector names that [List] implements, that we know do not
|
| * change the element type of the list, or let the list escape to code
|
| @@ -126,154 +136,672 @@
|
|
|
| bool _VERBOSE = false;
|
|
|
| -class ContainerTracerVisitor implements TypeInformationVisitor {
|
| - final ContainerTypeInformation container;
|
| - final TypeGraphInferrerEngine inferrer;
|
| +class InferrerEngineForContainerTracer
|
| + implements MinimalInferrerEngine<TypeMask> {
|
| final Compiler compiler;
|
|
|
| - // The set of [TypeInformation] where the traced container could
|
| - // flow in, and operations done on them.
|
| - final Set<TypeInformation> allUsers = new Set<TypeInformation>();
|
| + InferrerEngineForContainerTracer(this.compiler);
|
|
|
| - // The list of found assignments to the container.
|
| - final List<TypeInformation> assignments = <TypeInformation>[];
|
| + TypeMask typeOfElement(Element element) {
|
| + return compiler.typesTask.getGuaranteedTypeOfElement(element);
|
| + }
|
|
|
| - bool enableLengthTracking = true;
|
| - bool continueAnalyzing = true;
|
| + 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;
|
| - final Set<Element> analyzedElements = new Set<Element>();
|
|
|
| - ContainerTracerVisitor(this.container, inferrer)
|
| - : this.inferrer = inferrer, this.compiler = inferrer.compiler;
|
| + TypeMask potentialType;
|
| + int potentialLength;
|
| + bool isLengthTrackingDisabled = false;
|
| + bool continueAnalyzing = true;
|
|
|
| + TracerForConcreteContainer(ContainerTypeMask mask,
|
| + this.tracer,
|
| + this.compiler,
|
| + this.inferrer)
|
| + : analyzedNode = mask.allocationNode,
|
| + startElement = mask.allocationElement,
|
| + this.mask = mask;
|
| +
|
| void run() {
|
| - // 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);
|
| + int analysisCount = 0;
|
| + workList.add(startElement);
|
| while (!workList.isEmpty) {
|
| - 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) {
|
| + if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) {
|
| bailout('Too many users');
|
| break;
|
| }
|
| + Element currentElement = workList.removeLast().implementation;
|
| + new ContainerTracerVisitor(currentElement, this).run();
|
| + if (!continueAnalyzing) break;
|
| + analysisCount++;
|
| }
|
|
|
| - if (continueAnalyzing) {
|
| - for (TypeInformation info in allUsers) {
|
| - info.accept(this);
|
| - if (!continueAnalyzing) break;
|
| + if (!continueAnalyzing) {
|
| + if (mask.forwardTo == compiler.typesTask.fixedListType) {
|
| + mask.length = potentialLength;
|
| }
|
| + mask.elementType = compiler.typesTask.dynamicType;
|
| + return;
|
| }
|
|
|
| - ContainerTypeMask mask = container.type;
|
| - if (!enableLengthTracking
|
| - && (mask.forwardTo != compiler.typesTask.fixedListType)) {
|
| - mask.length = null;
|
| + // [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;
|
| }
|
|
|
| - TypeMask result = continueAnalyzing
|
| - ? inferrer.types.computeTypeMask(assignments)
|
| - : inferrer.types.dynamicType.type;
|
| -
|
| - mask.elementType = result;
|
| + // 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);
|
| + }
|
| if (_VERBOSE) {
|
| - print('$result and ${mask.length} '
|
| - 'for ${mask.allocationNode} ${mask.allocationElement}');
|
| + print('$potentialType and $potentialLength '
|
| + 'for $analyzedNode $startElement');
|
| }
|
| + mask.elementType = potentialType;
|
| + mask.length = potentialLength;
|
| }
|
|
|
| - void bailout(String reason) {
|
| + 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');
|
| + }
|
| + }
|
| +
|
| + 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) {
|
| if (_VERBOSE) {
|
| - ContainerTypeMask mask = container.type;
|
| - print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} '
|
| - 'because: $reason');
|
| + print('Bailout on $analyzedNode $startElement because of $reason');
|
| }
|
| continueAnalyzing = false;
|
| - enableLengthTracking = false;
|
| + return compiler.typesTask.dynamicType;
|
| }
|
|
|
| - visitNarrowTypeInformation(NarrowTypeInformation info) {}
|
| - visitPhiElementTypeInformation(PhiElementTypeInformation info) {}
|
| - visitElementInContainerTypeInformation(
|
| - ElementInContainerTypeInformation info) {}
|
| - visitContainerTypeInformation(ContainerTypeInformation info) {}
|
| - visitConcreteTypeInformation(ConcreteTypeInformation info) {}
|
| + 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);
|
| + }
|
| + }
|
|
|
| - visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) {
|
| - bailout('Passed to a closure');
|
| + void recordConstraint(Selector selector) {
|
| + constraints.add(selector);
|
| }
|
| +}
|
|
|
| - 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}');
|
| +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;
|
| + }
|
| }
|
|
|
| - visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) {
|
| - Selector selector = info.selector;
|
| - String selectorName = selector.name.slowToString();
|
| - 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]);
|
| + 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);
|
| }
|
| - } else if (selectorName == 'insert') {
|
| - if (positionalLength == 2) {
|
| - assignments.add(info.arguments.positional[1]);
|
| + if (index == 1 && isValueEscaping) {
|
| + tracer.addEscapingElement(parameter);
|
| }
|
| - } else {
|
| - bailout('Used in a not-ok selector');
|
| - return;
|
| - }
|
| - } else if (selector.isIndexSet()) {
|
| - assignments.add(info.arguments.positional[1]);
|
| - } else if (!selector.isIndex()) {
|
| - bailout('Used in a not-ok selector');
|
| - return;
|
| + index++;
|
| + });
|
| }
|
| }
|
| - if (!doNotChangeLengthSelectorsSet.contains(selectorName)) {
|
| - enableLengthTracking = false;
|
| + } else if (isReceiver) {
|
| + if (setterSelector.name == const SourceString('length')) {
|
| + tracer.disableLengthTracking();
|
| + tracer.unionPotentialTypeWith(compiler.typesTask.nullType);
|
| }
|
| - if (selectorName == 'length' && selector.isSetter()) {
|
| - enableLengthTracking = false;
|
| - assignments.add(inferrer.types.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);
|
| }
|
| - } else if (selector.isCall()
|
| - && !info.targets.every((element) => element.isFunction())) {
|
| - bailout('Passed to a closure');
|
| - return;
|
| }
|
| +
|
| + 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;
|
| }
|
|
|
| - bool isClosure(Element element) {
|
| - if (!element.isFunction()) return false;
|
| - Element outermost = element.getOutermostEnclosingMemberOrTopLevel();
|
| - return outermost.declaration != element.declaration;
|
| + 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;
|
| + }
|
| }
|
|
|
| - visitElementTypeInformation(ElementTypeInformation info) {
|
| - if (isClosure(info.element)) {
|
| - bailout('Returned from a closure');
|
| + 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);
|
| + 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 (isEscaping) {
|
| + return tracer.bailout('List containing itself');
|
| + }
|
| + tracer.unionPotentialTypeWith(argumentType);
|
| + } else {
|
| + return tracer.bailout('Send with the node as receiver $node');
|
| + }
|
| + } else if (!node.isPropertyAccess) {
|
| + visitArguments(node.arguments, selector);
|
| + }
|
| + 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;
|
| + }
|
| +
|
| + 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);
|
| + }
|
| +
|
| + return handleLoop(node, () {
|
| + visit(node.body);
|
| + });
|
| + }
|
| }
|
|
|