Index: sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart b/sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart |
deleted file mode 100644 |
index e5905ff2320ec7b1831296f39359afea7043de65..0000000000000000000000000000000000000000 |
--- a/sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart |
+++ /dev/null |
@@ -1,387 +0,0 @@ |
-// 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; |
- |
-// A set of selectors we know do not escape the elements inside the |
-// list. |
-Set<String> doesNotEscapeListSet = new Set<String>.from( |
- const <String>[ |
- // From Object. |
- '==', |
- 'hashCode', |
- 'toString', |
- 'noSuchMethod', |
- 'runtimeType', |
- |
- // From Iterable. |
- 'isEmpty', |
- 'isNotEmpty', |
- 'length', |
- 'any', |
- 'contains', |
- 'every', |
- 'join', |
- |
- // From List. |
- 'add', |
- 'addAll', |
- 'clear', |
- 'fillRange', |
- 'indexOf', |
- 'insert', |
- 'insertAll', |
- 'lastIndexOf', |
- 'remove', |
- 'removeRange', |
- 'replaceRange', |
- 'setAll', |
- 'setRange', |
- 'shuffle', |
- '[]=', |
- |
- // From JSArray. |
- 'checkMutable', |
- 'checkGrowable', |
- ]); |
- |
-Set<String> doesNotEscapeMapSet = new Set<String>.from( |
- const <String>[ |
- // From Object. |
- '==', |
- 'hashCode', |
- 'toString', |
- 'noSuchMethod', |
- 'runtimeType', |
- // from Map. |
- 'isEmpty', |
- 'isNotEmpty', |
- 'length', |
- 'clear', |
- 'containsKey', |
- 'containsValue', |
- '[]=', |
- // [keys] only allows key values to escape, which we do not track. |
- 'keys' |
- ]); |
- |
-abstract class TracerVisitor<T extends TypeInformation> |
- implements TypeInformationVisitor { |
- final T tracedType; |
- final TypeGraphInferrerEngine inferrer; |
- final Compiler compiler; |
- |
- static const int MAX_ANALYSIS_COUNT = 16; |
- final Setlet<Element> analyzedElements = new Setlet<Element>(); |
- |
- TracerVisitor(this.tracedType, inferrer) |
- : this.inferrer = inferrer, this.compiler = inferrer.compiler; |
- |
- // Work list that gets populated with [TypeInformation] that could |
- // contain the container. |
- final List<TypeInformation> workList = <TypeInformation>[]; |
- |
- // Work list of lists to analyze after analyzing the users of a |
- // [TypeInformation]. We know the [tracedType] has been stored in these |
- // lists and we must check how it escapes from these lists. |
- final List<ListTypeInformation> listsToAnalyze = |
- <ListTypeInformation>[]; |
- // Work list of maps to analyze after analyzing the users of a |
- // [TypeInformation]. We know the [tracedType] has been stored in these |
- // maps and we must check how it escapes from these maps. |
- final List<MapTypeInformation> mapsToAnalyze = <MapTypeInformation>[]; |
- |
- final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); |
- |
- // The current [TypeInformation] in the analysis. |
- TypeInformation currentUser; |
- bool continueAnalyzing = true; |
- |
- void addNewEscapeInformation(TypeInformation info) { |
- if (flowsInto.contains(info)) return; |
- flowsInto.add(info); |
- workList.add(info); |
- } |
- |
- void analyze() { |
- // Collect the [TypeInformation] where the list can flow in, |
- // as well as the operations done on all these [TypeInformation]s. |
- addNewEscapeInformation(tracedType); |
- while (!workList.isEmpty) { |
- currentUser = workList.removeLast(); |
- int expectedWork = analyzedElements.length + currentUser.users.length; |
- if (expectedWork > MAX_ANALYSIS_COUNT) { |
- bailout('Too many users'); |
- break; |
- } |
- for (TypeInformation info in currentUser.users) { |
- analyzedElements.add(info.owner); |
- info.accept(this); |
- } |
- while (!listsToAnalyze.isEmpty) { |
- analyzeStoredIntoList(listsToAnalyze.removeLast()); |
- } |
- while (!mapsToAnalyze.isEmpty) { |
- analyzeStoredIntoMap(mapsToAnalyze.removeLast()); |
- } |
- if (!continueAnalyzing) break; |
- } |
- } |
- |
- void bailout(String reason) { |
- if (_VERBOSE) { |
- print('Bailing out on $tracedType because: $reason'); |
- } |
- continueAnalyzing = false; |
- } |
- |
- void visitNarrowTypeInformation(NarrowTypeInformation info) { |
- addNewEscapeInformation(info); |
- } |
- |
- void visitPhiElementTypeInformation(PhiElementTypeInformation info) { |
- addNewEscapeInformation(info); |
- } |
- |
- void visitElementInContainerTypeInformation( |
- ElementInContainerTypeInformation info) { |
- addNewEscapeInformation(info); |
- } |
- |
- void visitKeyInMapTypeInformation(KeyInMapTypeInformation info) { |
- // We do not track the use of keys from a map, so we have to bail. |
- bailout('Used as key in Map'); |
- } |
- |
- void visitValueInMapTypeInformation(ValueInMapTypeInformation info) { |
- addNewEscapeInformation(info); |
- } |
- |
- void visitListTypeInformation(ListTypeInformation info) { |
- listsToAnalyze.add(info); |
- } |
- |
- void visitMapTypeInformation(MapTypeInformation info) { |
- mapsToAnalyze.add(info); |
- } |
- void visitConcreteTypeInformation(ConcreteTypeInformation info) {} |
- |
- void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) {} |
- |
- void visitClosureTypeInformation(ClosureTypeInformation info) {} |
- |
- void visitClosureCallSiteTypeInformation( |
- ClosureCallSiteTypeInformation info) {} |
- |
- visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
- Element called = info.calledElement; |
- if (inferrer.types.getInferredTypeOf(called) == currentUser) { |
- addNewEscapeInformation(info); |
- } |
- } |
- |
- void analyzeStoredIntoList(ListTypeInformation list) { |
- inferrer.analyzeListAndEnqueue(list); |
- if (list.bailedOut) { |
- bailout('Stored in a list that bailed out'); |
- } else { |
- list.flowsInto.forEach((flow) { |
- flow.users.forEach((user) { |
- if (user is !DynamicCallSiteTypeInformation) return; |
- if (user.receiver != flow) return; |
- if (inferrer._returnsListElementTypeSet.contains(user.selector)) { |
- addNewEscapeInformation(user); |
- } else if (!doesNotEscapeListSet.contains(user.selector.name)) { |
- bailout('Escape from a list via [${user.selector.name}]'); |
- } |
- }); |
- }); |
- } |
- } |
- |
- void analyzeStoredIntoMap(MapTypeInformation map) { |
- inferrer.analyzeMapAndEnqueue(map); |
- if (map.bailedOut) { |
- bailout('Stored in a map that bailed out'); |
- } else { |
- map.flowsInto.forEach((flow) { |
- flow.users.forEach((user) { |
- if (user is !DynamicCallSiteTypeInformation) return; |
- if (user.receiver != flow) return; |
- if (user.selector.isIndex) { |
- addNewEscapeInformation(user); |
- } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { |
- bailout('Escape from a map via [${user.selector.name}]'); |
- } |
- }); |
- }); |
- } |
- } |
- |
- /** |
- * Checks whether this is a call to a list adding method. The definition |
- * of what list adding means has to stay in sync with |
- * [isParameterOfListAddingMethod]. |
- */ |
- bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |
- if (info.arguments == null) return false; |
- var receiverType = info.receiver.type; |
- if (!receiverType.isContainer) return false; |
- String selectorName = info.selector.name; |
- List<TypeInformation> arguments = info.arguments.positional; |
- return (selectorName == '[]=' && currentUser == arguments[1]) |
- || (selectorName == 'insert' && currentUser == arguments[1]) |
- || (selectorName == 'add' && currentUser == arguments[0]); |
- } |
- |
- bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) { |
- if (info.arguments == null) return false; |
- var receiverType = info.receiver.type; |
- if (!receiverType.isMap) return false; |
- return info.selector.name == '[]='; |
- } |
- |
- /** |
- * Checks whether this is a call to a map adding method for values. The |
- * definition of map adding method has to stay in sync with |
- * [isParameterOfMapAddingMethod]. |
- */ |
- bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { |
- return isIndexSetOnMap(info) && |
- currentUser == info.arguments.positional[1]; |
- } |
- |
- /** |
- * Checks whether this is a call to a map adding method for keys. The |
- * definition of map adding method has to stay in sync with |
- * [isParameterOfMapAddingMethod]. |
- */ |
- bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) { |
- return isIndexSetOnMap(info) && |
- currentUser == info.arguments.positional[0]; |
- } |
- |
- void visitDynamicCallSiteTypeInformation( |
- DynamicCallSiteTypeInformation info) { |
- if (isAddedToContainer(info)) { |
- ContainerTypeMask mask = info.receiver.type; |
- |
- if (mask.allocationNode != null) { |
- ListTypeInformation list = |
- inferrer.types.allocatedLists[mask.allocationNode]; |
- listsToAnalyze.add(list); |
- } else { |
- // The [ContainerTypeMask] is a union of two containers, and |
- // we lose track of where these containers have been allocated |
- // at this point. |
- bailout('Stored in too many containers'); |
- } |
- } else if (isValueAddedToMap(info)) { |
- MapTypeMask mask = info.receiver.type; |
- if (mask.allocationNode != null) { |
- MapTypeInformation map = |
- inferrer.types.allocatedMaps[mask.allocationNode]; |
- mapsToAnalyze.add(map); |
- } else { |
- // The [MapTypeMask] is a union. See comment for |
- // [ContainerTypeMask] above. |
- bailout('Stored in too many maps'); |
- } |
- } else if (isKeyAddedToMap(info)) { |
- // We do not track the use of keys from a map, so we have to bail. |
- bailout('Used as key in Map'); |
- } |
- |
- if (info.targetsIncludeNoSuchMethod && |
- info.arguments != null && |
- info.arguments.contains(currentUser)) { |
- bailout('Passed to noSuchMethod'); |
- } |
- |
- Iterable<Element> inferredTargetTypes = info.targets.map((element) { |
- return inferrer.types.getInferredTypeOf(element); |
- }); |
- if (inferredTargetTypes.any((user) => user == currentUser)) { |
- addNewEscapeInformation(info); |
- } |
- } |
- |
- /** |
- * Check whether element is the parameter of a list adding method. |
- * The definition of what a list adding method is has to stay in sync with |
- * [isAddedToContainer]. |
- */ |
- bool isParameterOfListAddingMethod(Element element) { |
- if (!element.isParameter) return false; |
- if (element.enclosingClass != compiler.backend.listImplementation) { |
- return false; |
- } |
- Element method = element.enclosingElement; |
- return (method.name == '[]=') |
- || (method.name == 'add') |
- || (method.name == 'insert'); |
- } |
- |
- /** |
- * Check whether element is the parameter of a list adding method. |
- * The definition of what a list adding method is has to stay in sync with |
- * [isValueAddedToMap] and [isKeyAddedToMap]. |
- */ |
- bool isParameterOfMapAddingMethod(Element element) { |
- if (!element.isParameter) return false; |
- if (element.enclosingClass != compiler.backend.mapImplementation) { |
- return false; |
- } |
- Element method = element.enclosingElement; |
- return (method.name == '[]='); |
- } |
- |
- bool isClosure(Element element) { |
- if (!element.isFunction) return false; |
- /// Creating an instance of a class that implements [Function] also |
- /// closurizes the corresponding [call] member. We do not currently |
- /// track these, thus the check for [isClosurized] on such a method will |
- /// return false. Instead we catch that case here for now. |
- // TODO(herhut): Handle creation of closures from instances of Function. |
- if (element.isInstanceMember && |
- element.name == Compiler.CALL_OPERATOR_NAME) { |
- return true; |
- } |
- Element outermost = element.outermostEnclosingMemberOrTopLevel; |
- return outermost.declaration != element.declaration; |
- } |
- |
- void visitMemberTypeInformation(MemberTypeInformation info) { |
- Element element = info.element; |
- if (info.isClosurized) { |
- bailout('Returned from a closurized method'); |
- } |
- if (isClosure(info.element)) { |
- bailout('Returned from a closure'); |
- } |
- if (!inferrer.compiler.backend |
- .canBeUsedForGlobalOptimizations(info.element)) { |
- bailout('Escape to code that has special backend treatment'); |
- } |
- addNewEscapeInformation(info); |
- } |
- |
- void visitParameterTypeInformation(ParameterTypeInformation info) { |
- ParameterElement element = info.element; |
- if (inferrer.isNativeElement(element.functionDeclaration)) { |
- bailout('Passed to a native method'); |
- } |
- if (!inferrer.compiler.backend |
- .canBeUsedForGlobalOptimizations(info.element)) { |
- bailout('Escape to code that has special backend treatment'); |
- } |
- if (isParameterOfListAddingMethod(info.element) || |
- isParameterOfMapAddingMethod(info.element)) { |
- // These elements are being handled in |
- // [visitDynamicCallSiteTypeInformation]. |
- return; |
- } |
- addNewEscapeInformation(info); |
- } |
-} |