| Index: sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/inferrer/container_tracer.dart (revision 27995)
|
| +++ 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,154 @@
|
|
|
| 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 enableLengthTracking = true;
|
| + 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);
|
| + // 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');
|
| + 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;
|
| + 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) {}
|
|
|
| - 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));
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * 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++;
|
| + 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}');
|
| }
|
| - 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);
|
| + 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]);
|
| }
|
| - if (index == 1 && isValueEscaping) {
|
| - tracer.addEscapingElement(parameter);
|
| + } else if (selectorName == 'insert') {
|
| + if (positionalLength == 2) {
|
| + assignments.add(info.arguments.positional[1]);
|
| }
|
| - index++;
|
| - });
|
| + } 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;
|
| }
|
| }
|
| - } else if (isReceiver) {
|
| - if (setterSelector.name == const SourceString('length')) {
|
| - tracer.disableLengthTracking();
|
| - tracer.unionPotentialTypeWith(compiler.typesTask.nullType);
|
| + if (!doNotChangeLengthSelectorsSet.contains(selectorName)) {
|
| + enableLengthTracking = false;
|
| }
|
| - } 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);
|
| + 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;
|
| }
|
| -
|
| - 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);
|
| - });
|
| - }
|
| }
|
|
|