| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of type_graph_inferrer; |
| |
| class ClosureTracerVisitor extends TracerVisitor<ApplyableTypeInformation> { |
| final Iterable<FunctionElement> tracedElements; |
| |
| ClosureTracerVisitor(this.tracedElements, tracedType, inferrer) |
| : super(tracedType, inferrer); |
| |
| void run() { |
| for (FunctionElement e in tracedElements) { |
| e.functionSignature.forEachParameter((Element parameter) { |
| ElementTypeInformation info = |
| inferrer.types.getInferredTypeOf(parameter); |
| info.abandonInferencing = info.abandonInferencing && |
| !info.mightResume; |
| }); |
| } |
| analyze(); |
| for(FunctionElement e in tracedElements) { |
| e.functionSignature.forEachParameter((Element parameter) { |
| ElementTypeInformation info = |
| inferrer.types.getInferredTypeOf(parameter); |
| if (continueAnalyzing) { |
| info.disableInferenceForClosures = false; |
| } else { |
| info.giveUp(inferrer); |
| } |
| }); |
| } |
| } |
| |
| void tagAsFunctionApplyTarget([String reason]) { |
| tracedType.mightBePassedToFunctionApply = true; |
| if (_VERBOSE) { |
| print("Closure $tracedType might be passed to apply: $reason"); |
| } |
| } |
| |
| void analyzeCall(CallSiteTypeInformation info) { |
| Selector selector = info.selector; |
| tracedElements.forEach((FunctionElement functionElement) { |
| if (!selector.signatureApplies(functionElement, compiler)) return; |
| inferrer.updateParameterAssignments(info, functionElement, info.arguments, |
| selector, remove: false, addToQueue: false); |
| }); |
| } |
| |
| visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| super.visitClosureCallSiteTypeInformation(info); |
| if (info.closure == currentUser) { |
| analyzeCall(info); |
| } else { |
| bailout('Passed to a closure'); |
| } |
| } |
| |
| visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| super.visitStaticCallSiteTypeInformation(info); |
| Element called = info.calledElement; |
| if (called.isForeign(compiler)) { |
| String name = called.name; |
| if (name == 'JS' || name == 'DART_CLOSURE_TO_JS') { |
| bailout('Used in JS ${info.call}'); |
| } |
| } |
| if (called.isGetter |
| && info.selector != null |
| && info.selector.isCall |
| && inferrer.types.getInferredTypeOf(called) == currentUser) { |
| // This node can be a closure call as well. For example, `foo()` |
| // where `foo` is a getter. |
| analyzeCall(info); |
| } |
| if (checkIfFunctionApply(called) && |
| info.arguments != null && |
| info.arguments.contains(currentUser)) { |
| tagAsFunctionApplyTarget("static call"); |
| } |
| } |
| |
| bool checkIfCurrentUser(element) { |
| return inferrer.types.getInferredTypeOf(element) == currentUser; |
| } |
| |
| bool checkIfFunctionApply(element) { |
| return compiler.functionApplyMethod == element; |
| } |
| |
| visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| super.visitDynamicCallSiteTypeInformation(info); |
| if (info.selector.isCall) { |
| if (info.arguments.contains(currentUser)) { |
| if (!info.targets.every((element) => element.isFunction)) { |
| bailout('Passed to a closure'); |
| } |
| if (info.targets.any(checkIfFunctionApply)) { |
| tagAsFunctionApplyTarget("dynamic call"); |
| } |
| } else if (info.targets.any((element) => checkIfCurrentUser(element))) { |
| analyzeCall(info); |
| } |
| } else if (info.selector.isGetter && |
| info.selector.name == Compiler.CALL_OPERATOR_NAME) { |
| // We are potentially tearing off ourself here |
| addNewEscapeInformation(info); |
| } |
| } |
| } |
| |
| class StaticTearOffClosureTracerVisitor extends ClosureTracerVisitor { |
| StaticTearOffClosureTracerVisitor(tracedElement, tracedType, inferrer) |
| : super([tracedElement], tracedType, inferrer); |
| |
| visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| super.visitStaticCallSiteTypeInformation(info); |
| if (info.calledElement == tracedElements.first |
| && info.selector != null |
| && info.selector.isGetter) { |
| addNewEscapeInformation(info); |
| } |
| } |
| } |