Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart |
| =================================================================== |
| --- sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (revision 27949) |
| +++ sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (working copy) |
| @@ -2,18 +2,8 @@ |
| // 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; |
| +part of type_graph_inferrer; |
| -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 |
| @@ -136,672 +126,149 @@ |
| 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); |
| - } |
| + bool disableLengthTracking = false; |
|
kasperl
2013/09/27 05:35:43
Consider making this positive instead: enableLengt
ngeoffray
2013/09/27 07:47:21
Done.
|
| + bool continueAnalyzing = true; |
| - 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>(); |
| - TypeMask potentialType; |
| - int potentialLength; |
| - bool isLengthTrackingDisabled = false; |
| - bool continueAnalyzing = true; |
| + ContainerTracerVisitor(this.container, inferrer) |
| + : this.inferrer = inferrer, this.compiler = inferrer.compiler; |
| - TracerForConcreteContainer(ContainerTypeMask mask, |
| - this.tracer, |
| - this.compiler, |
| - this.inferrer) |
| - : analyzedNode = mask.allocationNode, |
| - startElement = mask.allocationElement, |
| - this.mask = mask; |
| - |
| void run() { |
| - int analysisCount = 0; |
| - workList.add(startElement); |
| - while (!workList.isEmpty) { |
| - if (workList.length + analysisCount > MAX_ANALYSIS_COUNT) { |
| + // 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> workQueue = <TypeInformation>[]; |
|
kasperl
2013/09/27 05:35:43
I'd rename this to worklist because you're not usi
ngeoffray
2013/09/27 07:47:21
Done.
|
| + allUsers.add(container); |
| + workQueue.add(container); |
| + while (!workQueue.isEmpty) { |
| + TypeInformation user = workQueue.removeLast(); |
| + user.users.forEach((TypeInformation info) { |
| + if (allUsers.contains(info)) return; |
| + allUsers.add(info); |
| + analyzedElements.add(info.owner); |
| + if (info.reachedBy(user, inferrer)) { |
| + workQueue.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 (disableLengthTracking |
| + && (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'); |
| + print('$result and ${mask.length} ' |
| + 'for ${mask.allocationNode} ${mask.allocationElement}'); |
| } |
| - 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'); |
| - } |
| - } |
| - |
| - 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; |
| + disableLengthTracking = true; |
| } |
| - 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) {} |
| - void recordConstraint(Selector selector) { |
| - constraints.add(selector); |
| + visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| + bailout('Passed to a closure'); |
| } |
| -} |
| -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)); |
| - }); |
| + 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}'); |
| + } |
| } |
| - /** |
| - * 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++; |
| + visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| + Selector selector = info.selector; |
| + String selectorName = selector.name.slowToString(); |
| + if (allUsers.contains(info.receiver)) { |
| + if (!okSelectorsSet.contains(selectorName)) { |
| + if (selector.isCall() |
| + && (selectorName == 'add' || selectorName == 'insert')) { |
| + int positionalLength = info.arguments.positional.length; |
|
kasperl
2013/09/27 05:35:43
Would it be terrible to always grab hold of the nu
ngeoffray
2013/09/27 07:47:21
Done.
|
| + if (selectorName == 'add' && positionalLength == 1) { |
| + assignments.add(info.arguments.positional[0]); |
| + } else if (selectorName == 'insert' |
| + && (positionalLength == 1 || positionalLength == 2)) { |
| + assignments.add(info.arguments.positional[1]); |
| } |
| - parameterIndex++; |
| - }); |
| - if (index != indices.length) { |
| - tracer.bailout('Used in a named parameter or closure'); |
| + } else if (selector.isIndexSet()) { |
| + assignments.add(info.arguments.positional[1]); |
| + } else if (!selector.isIndex()) { |
| + bailout('Used in a not-ok selector'); |
| + return; |
| } |
| } |
| - 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++; |
| + if (!doNotChangeLengthSelectorsSet.contains(selectorName)) { |
| + disableLengthTracking = true; |
| } |
| - 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); |
| - }); |
| + if (selectorName == 'length' && selector.isSetter()) { |
| + disableLengthTracking = true; |
| + assignments.add(inferrer.types.nullType); |
| } |
| - } 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); |
| - }); |
| + } else if (selector.isCall() |
| + && !info.targets.every((element) => element.isFunction())) { |
| + bailout('Passed to a closure'); |
| + return; |
| } |
| - |
| - 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; |
| - } |
| + bool isClosure(Element element) { |
| + if (!element.isFunction()) return false; |
| + Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
| + return outermost.declaration != element.declaration; |
| } |
| - 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); |
| + visitElementTypeInformation(ElementTypeInformation info) { |
| + if (isClosure(info.element)) { |
| + bailout('Returned from a closure'); |
| } |
| - |
| - 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); |
| - }); |
| - } |
| } |