| // 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. |
| |
| library compiler.src.inferrer.closure_tracer; |
| |
| import '../common/names.dart' show Identifiers, Names; |
| import '../elements/entities.dart'; |
| import 'debug.dart' as debug; |
| import 'engine.dart'; |
| import 'node_tracer.dart'; |
| import 'type_graph_nodes.dart'; |
| |
| class ClosureTracerVisitor extends TracerVisitor { |
| final Iterable<FunctionEntity> tracedElements; |
| final List<CallSiteTypeInformation> _callsToAnalyze = |
| <CallSiteTypeInformation>[]; |
| |
| ClosureTracerVisitor(this.tracedElements, ApplyableTypeInformation tracedType, |
| InferrerEngine inferrer) |
| : super(tracedType, inferrer) { |
| assert( |
| tracedElements.every((f) => !f.isAbstract), |
| "Tracing abstract methods: " |
| "${tracedElements.where((f) => f.isAbstract)}"); |
| } |
| |
| @override |
| ApplyableTypeInformation get tracedType => |
| super.tracedType as ApplyableTypeInformation; |
| |
| void run() { |
| analyze(); |
| if (!continueAnalyzing) return; |
| _callsToAnalyze.forEach(_analyzeCall); |
| for (FunctionEntity element in tracedElements) { |
| inferrer.types.strategy.forEachParameter(element, (Local parameter) { |
| ElementTypeInformation info = |
| inferrer.types.getInferredTypeOfParameter(parameter); |
| info.disableInferenceForClosures = false; |
| }); |
| } |
| } |
| |
| void _tagAsFunctionApplyTarget([String? reason]) { |
| tracedType.mightBePassedToFunctionApply = true; |
| if (debug.VERBOSE) { |
| print("Closure $tracedType might be passed to apply: $reason"); |
| } |
| } |
| |
| void _registerCallForLaterAnalysis(CallSiteTypeInformation info) { |
| _callsToAnalyze.add(info); |
| } |
| |
| void _analyzeCall(CallSiteTypeInformation info) { |
| final selector = info.selector!; |
| tracedElements.forEach((FunctionEntity functionElement) { |
| if (!selector.callStructure |
| .signatureApplies(functionElement.parameterStructure)) { |
| return; |
| } |
| inferrer.updateParameterInputs( |
| info, functionElement, info.arguments, selector, |
| remove: false, addToQueue: false); |
| }); |
| } |
| |
| @override |
| void visitClosureCallSiteTypeInformation( |
| ClosureCallSiteTypeInformation info) { |
| super.visitClosureCallSiteTypeInformation(info); |
| if (info.closure == currentUser) { |
| _registerCallForLaterAnalysis(info); |
| } else { |
| bailout('Passed to a closure'); |
| } |
| } |
| |
| @override |
| void visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| super.visitStaticCallSiteTypeInformation(info); |
| MemberEntity called = info.calledElement; |
| if (inferrer.closedWorld.commonElements.isForeign(called)) { |
| final name = called.name!; |
| if (name == Identifiers.JS || name == Identifiers.DART_CLOSURE_TO_JS) { |
| bailout('Used in JS ${info.debugName}'); |
| } else if (name == Identifiers.RAW_DART_FUNCTION_REF) { |
| bailout('Escaped raw function reference'); |
| } |
| } |
| |
| final selector = info.selector; |
| if (called.isGetter && |
| selector != null && |
| selector.isCall && |
| inferrer.types.getInferredTypeOfMember(called) == currentUser) { |
| // This node can be a closure call as well. For example, `foo()` |
| // where `foo` is a getter. |
| _registerCallForLaterAnalysis(info); |
| } |
| |
| final arguments = info.arguments; |
| if (_checkIfFunctionApply(called) && |
| arguments != null && |
| arguments.contains(currentUser)) { |
| _tagAsFunctionApplyTarget("static call"); |
| } |
| } |
| |
| bool _checkIfFunctionApply(MemberEntity element) { |
| return inferrer.closedWorld.commonElements.isFunctionApplyMethod(element); |
| } |
| |
| @override |
| void visitDynamicCallSiteTypeInformation( |
| DynamicCallSiteTypeInformation info) { |
| super.visitDynamicCallSiteTypeInformation(info); |
| final selector = info.selector!; |
| final user = currentUser; |
| if (selector.isCall) { |
| if (info.arguments!.contains(user)) { |
| if (info.hasClosureCallTargets || dynamicCallTargetsNonFunction(info)) { |
| bailout('Passed to a closure'); |
| } |
| if (info.targets.any((target) => inferrer.memberHierarchyBuilder |
| .anyTargetMember(target, _checkIfFunctionApply))) { |
| _tagAsFunctionApplyTarget("dynamic call"); |
| } |
| } else { |
| if (user is MemberTypeInformation) { |
| final currentUserMember = user.member; |
| if (info.targets.any((target) => inferrer.memberHierarchyBuilder |
| .anyTargetMember( |
| target, (element) => element == currentUserMember))) { |
| _registerCallForLaterAnalysis(info); |
| } |
| } |
| } |
| } else if (selector.isGetter && selector.memberName == Names.call) { |
| // We are potentially tearing off ourself here |
| addNewEscapeInformation(info); |
| } |
| } |
| } |
| |
| class StaticTearOffClosureTracerVisitor extends ClosureTracerVisitor { |
| StaticTearOffClosureTracerVisitor(FunctionEntity tracedElement, |
| ApplyableTypeInformation tracedType, InferrerEngine inferrer) |
| : super([tracedElement], tracedType, inferrer); |
| |
| @override |
| void visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| super.visitStaticCallSiteTypeInformation(info); |
| |
| final selector = info.selector; |
| if (info.calledElement == tracedElements.first && |
| selector != null && |
| selector.isGetter) { |
| addNewEscapeInformation(info); |
| } |
| } |
| } |