blob: 52c5dcacfc0f011ef7012b596a4c97a1b39aa962 [file] [log] [blame]
// 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);
}
}
}