| OLD | NEW | 
|    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |    2 // for details. All rights reserved. Use of this source code is governed by a | 
|    3 // BSD-style license that can be found in the LICENSE file. |    3 // BSD-style license that can be found in the LICENSE file. | 
|    4  |    4  | 
|    5 library compiler.src.inferrer.node_tracer; |    5 library compiler.src.inferrer.node_tracer; | 
|    6  |    6  | 
|    7 import '../common/names.dart' show Identifiers; |    7 import '../common/names.dart' show Identifiers; | 
|    8 import '../compiler.dart' show Compiler; |    8 import '../compiler.dart' show Compiler; | 
|    9 import '../elements/elements.dart'; |    9 import '../elements/elements.dart'; | 
|   10 import '../types/types.dart' show ContainerTypeMask, MapTypeMask; |   10 import '../types/types.dart' show ContainerTypeMask, MapTypeMask; | 
|   11 import '../util/util.dart' show Setlet; |   11 import '../util/util.dart' show Setlet; | 
|   12  |   12  | 
|   13 import 'type_graph_inferrer.dart' show TypeGraphInferrerEngine; |   13 import 'type_graph_inferrer.dart' show TypeGraphInferrerEngine; | 
|   14 import 'type_graph_nodes.dart'; |   14 import 'type_graph_nodes.dart'; | 
|   15 import 'debug.dart' as debug; |   15 import 'debug.dart' as debug; | 
|   16  |   16  | 
|   17 // A set of selectors we know do not escape the elements inside the |   17 // A set of selectors we know do not escape the elements inside the | 
|   18 // list. |   18 // list. | 
|   19 Set<String> doesNotEscapeListSet = new Set<String>.from( |   19 Set<String> doesNotEscapeListSet = new Set<String>.from(const <String>[ | 
|   20   const <String>[ |   20   // From Object. | 
|   21     // From Object. |   21   '==', | 
|   22     '==', |   22   'hashCode', | 
|   23     'hashCode', |   23   'toString', | 
|   24     'toString', |   24   'noSuchMethod', | 
|   25     'noSuchMethod', |   25   'runtimeType', | 
|   26     'runtimeType', |  | 
|   27  |   26  | 
|   28     // From Iterable. |   27   // From Iterable. | 
|   29     'isEmpty', |   28   'isEmpty', | 
|   30     'isNotEmpty', |   29   'isNotEmpty', | 
|   31     'length', |   30   'length', | 
|   32     'contains', |   31   'contains', | 
|   33     'join', |   32   'join', | 
|   34  |   33  | 
|   35     // From List. |   34   // From List. | 
|   36     'add', |   35   'add', | 
|   37     'addAll', |   36   'addAll', | 
|   38     'clear', |   37   'clear', | 
|   39     'fillRange', |   38   'fillRange', | 
|   40     'indexOf', |   39   'indexOf', | 
|   41     'insert', |   40   'insert', | 
|   42     'insertAll', |   41   'insertAll', | 
|   43     'lastIndexOf', |   42   'lastIndexOf', | 
|   44     'remove', |   43   'remove', | 
|   45     'removeRange', |   44   'removeRange', | 
|   46     'replaceRange', |   45   'replaceRange', | 
|   47     'setAll', |   46   'setAll', | 
|   48     'setRange', |   47   'setRange', | 
|   49     'shuffle', |   48   'shuffle', | 
|   50     '[]=', |   49   '[]=', | 
|   51  |   50  | 
|   52     // From JSArray. |   51   // From JSArray. | 
|   53     'checkMutable', |   52   'checkMutable', | 
|   54     'checkGrowable', |   53   'checkGrowable', | 
|   55   ]); |   54 ]); | 
|   56  |   55  | 
|   57 Set<String> doesNotEscapeMapSet = new Set<String>.from( |   56 Set<String> doesNotEscapeMapSet = new Set<String>.from(const <String>[ | 
|   58   const <String>[ |   57   // From Object. | 
|   59     // From Object. |   58   '==', | 
|   60     '==', |   59   'hashCode', | 
|   61     'hashCode', |   60   'toString', | 
|   62     'toString', |   61   'noSuchMethod', | 
|   63     'noSuchMethod', |   62   'runtimeType', | 
|   64     'runtimeType', |   63   // from Map. | 
|   65     // from Map. |   64   'isEmpty', | 
|   66     'isEmpty', |   65   'isNotEmpty', | 
|   67     'isNotEmpty', |   66   'length', | 
|   68     'length', |   67   'clear', | 
|   69     'clear', |   68   'containsKey', | 
|   70     'containsKey', |   69   'containsValue', | 
|   71     'containsValue', |   70   '[]=', | 
|   72     '[]=', |   71   // [keys] only allows key values to escape, which we do not track. | 
|   73     // [keys] only allows key values to escape, which we do not track. |   72   'keys' | 
|   74     'keys' |   73 ]); | 
|   75   ]); |  | 
|   76  |   74  | 
|   77 /// Common logic to trace a value through the type inference graph nodes. |   75 /// Common logic to trace a value through the type inference graph nodes. | 
|   78 abstract class TracerVisitor<T extends TypeInformation> |   76 abstract class TracerVisitor<T extends TypeInformation> | 
|   79     implements TypeInformationVisitor { |   77     implements TypeInformationVisitor { | 
|   80   final T tracedType; |   78   final T tracedType; | 
|   81   final TypeGraphInferrerEngine inferrer; |   79   final TypeGraphInferrerEngine inferrer; | 
|   82   final Compiler compiler; |   80   final Compiler compiler; | 
|   83  |   81  | 
|   84   static const int MAX_ANALYSIS_COUNT = 16; |   82   static const int MAX_ANALYSIS_COUNT = 16; | 
|   85   final Setlet<Element> analyzedElements = new Setlet<Element>(); |   83   final Setlet<Element> analyzedElements = new Setlet<Element>(); | 
|   86  |   84  | 
|   87   TracerVisitor(this.tracedType, TypeGraphInferrerEngine inferrer) |   85   TracerVisitor(this.tracedType, TypeGraphInferrerEngine inferrer) | 
|   88       : this.inferrer = inferrer, this.compiler = inferrer.compiler; |   86       : this.inferrer = inferrer, | 
 |   87         this.compiler = inferrer.compiler; | 
|   89  |   88  | 
|   90   // Work list that gets populated with [TypeInformation] that could |   89   // Work list that gets populated with [TypeInformation] that could | 
|   91   // contain the container. |   90   // contain the container. | 
|   92   final List<TypeInformation> workList = <TypeInformation>[]; |   91   final List<TypeInformation> workList = <TypeInformation>[]; | 
|   93  |   92  | 
|   94   // Work list of lists to analyze after analyzing the users of a |   93   // Work list of lists to analyze after analyzing the users of a | 
|   95   // [TypeInformation]. We know the [tracedType] has been stored in these |   94   // [TypeInformation]. We know the [tracedType] has been stored in these | 
|   96   // lists and we must check how it escapes from these lists. |   95   // lists and we must check how it escapes from these lists. | 
|   97   final List<ListTypeInformation> listsToAnalyze = |   96   final List<ListTypeInformation> listsToAnalyze = <ListTypeInformation>[]; | 
|   98       <ListTypeInformation>[]; |  | 
|   99   // Work list of maps to analyze after analyzing the users of a |   97   // Work list of maps to analyze after analyzing the users of a | 
|  100   // [TypeInformation]. We know the [tracedType] has been stored in these |   98   // [TypeInformation]. We know the [tracedType] has been stored in these | 
|  101   // maps and we must check how it escapes from these maps. |   99   // maps and we must check how it escapes from these maps. | 
|  102   final List<MapTypeInformation> mapsToAnalyze = <MapTypeInformation>[]; |  100   final List<MapTypeInformation> mapsToAnalyze = <MapTypeInformation>[]; | 
|  103  |  101  | 
|  104   final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); |  102   final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); | 
|  105  |  103  | 
|  106   // The current [TypeInformation] in the analysis. |  104   // The current [TypeInformation] in the analysis. | 
|  107   TypeInformation currentUser; |  105   TypeInformation currentUser; | 
|  108   bool continueAnalyzing = true; |  106   bool continueAnalyzing = true; | 
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  179     addNewEscapeInformation(info); |  177     addNewEscapeInformation(info); | 
|  180   } |  178   } | 
|  181  |  179  | 
|  182   void visitListTypeInformation(ListTypeInformation info) { |  180   void visitListTypeInformation(ListTypeInformation info) { | 
|  183     listsToAnalyze.add(info); |  181     listsToAnalyze.add(info); | 
|  184   } |  182   } | 
|  185  |  183  | 
|  186   void visitMapTypeInformation(MapTypeInformation info) { |  184   void visitMapTypeInformation(MapTypeInformation info) { | 
|  187     mapsToAnalyze.add(info); |  185     mapsToAnalyze.add(info); | 
|  188   } |  186   } | 
 |  187  | 
|  189   void visitConcreteTypeInformation(ConcreteTypeInformation info) {} |  188   void visitConcreteTypeInformation(ConcreteTypeInformation info) {} | 
|  190  |  189  | 
|  191   void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) {} |  190   void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) {} | 
|  192  |  191  | 
|  193   void visitBoolLiteralTypeInformation(BoolLiteralTypeInformation info) {} |  192   void visitBoolLiteralTypeInformation(BoolLiteralTypeInformation info) {} | 
|  194  |  193  | 
|  195   void visitClosureTypeInformation(ClosureTypeInformation info) {} |  194   void visitClosureTypeInformation(ClosureTypeInformation info) {} | 
|  196  |  195  | 
|  197   void visitClosureCallSiteTypeInformation( |  196   void visitClosureCallSiteTypeInformation( | 
|  198       ClosureCallSiteTypeInformation info) {} |  197       ClosureCallSiteTypeInformation info) {} | 
|  199  |  198  | 
|  200   visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |  199   visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 
|  201     Element called = info.calledElement; |  200     Element called = info.calledElement; | 
|  202     if (inferrer.types.getInferredTypeOf(called) == currentUser) { |  201     if (inferrer.types.getInferredTypeOf(called) == currentUser) { | 
|  203       addNewEscapeInformation(info); |  202       addNewEscapeInformation(info); | 
|  204     } |  203     } | 
|  205   } |  204   } | 
|  206  |  205  | 
|  207   void analyzeStoredIntoList(ListTypeInformation list) { |  206   void analyzeStoredIntoList(ListTypeInformation list) { | 
|  208     inferrer.analyzeListAndEnqueue(list); |  207     inferrer.analyzeListAndEnqueue(list); | 
|  209     if (list.bailedOut) { |  208     if (list.bailedOut) { | 
|  210       bailout('Stored in a list that bailed out'); |  209       bailout('Stored in a list that bailed out'); | 
|  211     } else { |  210     } else { | 
|  212       list.flowsInto.forEach((flow) { |  211       list.flowsInto.forEach((flow) { | 
|  213         flow.users.forEach((user) { |  212         flow.users.forEach((user) { | 
|  214           if (user is !DynamicCallSiteTypeInformation) return; |  213           if (user is! DynamicCallSiteTypeInformation) return; | 
|  215           if (user.receiver != flow) return; |  214           if (user.receiver != flow) return; | 
|  216           if (inferrer.returnsListElementTypeSet.contains(user.selector)) { |  215           if (inferrer.returnsListElementTypeSet.contains(user.selector)) { | 
|  217             addNewEscapeInformation(user); |  216             addNewEscapeInformation(user); | 
|  218           } else if (!doesNotEscapeListSet.contains(user.selector.name)) { |  217           } else if (!doesNotEscapeListSet.contains(user.selector.name)) { | 
|  219             bailout('Escape from a list via [${user.selector.name}]'); |  218             bailout('Escape from a list via [${user.selector.name}]'); | 
|  220           } |  219           } | 
|  221         }); |  220         }); | 
|  222       }); |  221       }); | 
|  223     } |  222     } | 
|  224   } |  223   } | 
|  225  |  224  | 
|  226   void analyzeStoredIntoMap(MapTypeInformation map) { |  225   void analyzeStoredIntoMap(MapTypeInformation map) { | 
|  227     inferrer.analyzeMapAndEnqueue(map); |  226     inferrer.analyzeMapAndEnqueue(map); | 
|  228     if (map.bailedOut) { |  227     if (map.bailedOut) { | 
|  229       bailout('Stored in a map that bailed out'); |  228       bailout('Stored in a map that bailed out'); | 
|  230     } else { |  229     } else { | 
|  231       map.flowsInto.forEach((flow) { |  230       map.flowsInto.forEach((flow) { | 
|  232         flow.users.forEach((user) { |  231         flow.users.forEach((user) { | 
|  233           if (user is !DynamicCallSiteTypeInformation) return; |  232           if (user is! DynamicCallSiteTypeInformation) return; | 
|  234           if (user.receiver != flow) return; |  233           if (user.receiver != flow) return; | 
|  235           if (user.selector.isIndex) { |  234           if (user.selector.isIndex) { | 
|  236             addNewEscapeInformation(user); |  235             addNewEscapeInformation(user); | 
|  237           } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { |  236           } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { | 
|  238             bailout('Escape from a map via [${user.selector.name}]'); |  237             bailout('Escape from a map via [${user.selector.name}]'); | 
|  239           } |  238           } | 
|  240         }); |  239         }); | 
|  241       }); |  240       }); | 
|  242     } |  241     } | 
|  243   } |  242   } | 
|  244  |  243  | 
|  245   /** |  244   /** | 
|  246    * Checks whether this is a call to a list adding method. The definition |  245    * Checks whether this is a call to a list adding method. The definition | 
|  247    * of what list adding means has to stay in sync with |  246    * of what list adding means has to stay in sync with | 
|  248    * [isParameterOfListAddingMethod]. |  247    * [isParameterOfListAddingMethod]. | 
|  249    */ |  248    */ | 
|  250   bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |  249   bool isAddedToContainer(DynamicCallSiteTypeInformation info) { | 
|  251     if (info.arguments == null) return false; |  250     if (info.arguments == null) return false; | 
|  252     var receiverType = info.receiver.type; |  251     var receiverType = info.receiver.type; | 
|  253     if (!receiverType.isContainer) return false; |  252     if (!receiverType.isContainer) return false; | 
|  254     String selectorName = info.selector.name; |  253     String selectorName = info.selector.name; | 
|  255     List<TypeInformation> arguments = info.arguments.positional; |  254     List<TypeInformation> arguments = info.arguments.positional; | 
|  256     return (selectorName == '[]=' && currentUser == arguments[1]) |  255     return (selectorName == '[]=' && currentUser == arguments[1]) || | 
|  257         || (selectorName == 'insert' && currentUser == arguments[1]) |  256         (selectorName == 'insert' && currentUser == arguments[1]) || | 
|  258         || (selectorName == 'add' && currentUser == arguments[0]); |  257         (selectorName == 'add' && currentUser == arguments[0]); | 
|  259   } |  258   } | 
|  260  |  259  | 
|  261   bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) { |  260   bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) { | 
|  262     if (info.arguments == null) return false; |  261     if (info.arguments == null) return false; | 
|  263     var receiverType = info.receiver.type; |  262     var receiverType = info.receiver.type; | 
|  264     if (!receiverType.isMap) return false; |  263     if (!receiverType.isMap) return false; | 
|  265     return info.selector.name == '[]='; |  264     return info.selector.name == '[]='; | 
|  266   } |  265   } | 
|  267  |  266  | 
|  268   /** |  267   /** | 
|  269    * Checks whether this is a call to a map adding method for values. The |  268    * Checks whether this is a call to a map adding method for values. The | 
|  270    * definition of map adding method has to stay in sync with |  269    * definition of map adding method has to stay in sync with | 
|  271    * [isParameterOfMapAddingMethod]. |  270    * [isParameterOfMapAddingMethod]. | 
|  272    */ |  271    */ | 
|  273   bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { |  272   bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { | 
|  274      return isIndexSetOnMap(info) && |  273     return isIndexSetOnMap(info) && currentUser == info.arguments.positional[1]; | 
|  275          currentUser == info.arguments.positional[1]; |  | 
|  276   } |  274   } | 
|  277  |  275  | 
|  278   /** |  276   /** | 
|  279    * Checks whether this is a call to a map adding method for keys. The |  277    * Checks whether this is a call to a map adding method for keys. The | 
|  280    * definition of map adding method has to stay in sync with |  278    * definition of map adding method has to stay in sync with | 
|  281    * [isParameterOfMapAddingMethod]. |  279    * [isParameterOfMapAddingMethod]. | 
|  282    */ |  280    */ | 
|  283   bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) { |  281   bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) { | 
|  284     return isIndexSetOnMap(info) && |  282     return isIndexSetOnMap(info) && currentUser == info.arguments.positional[0]; | 
|  285         currentUser == info.arguments.positional[0]; |  | 
|  286   } |  283   } | 
|  287  |  284  | 
|  288   void visitDynamicCallSiteTypeInformation( |  285   void visitDynamicCallSiteTypeInformation( | 
|  289       DynamicCallSiteTypeInformation info) { |  286       DynamicCallSiteTypeInformation info) { | 
|  290     if (isAddedToContainer(info)) { |  287     if (isAddedToContainer(info)) { | 
|  291       ContainerTypeMask mask = info.receiver.type; |  288       ContainerTypeMask mask = info.receiver.type; | 
|  292  |  289  | 
|  293       if (mask.allocationNode != null) { |  290       if (mask.allocationNode != null) { | 
|  294         ListTypeInformation list = |  291         ListTypeInformation list = | 
|  295             inferrer.types.allocatedLists[mask.allocationNode]; |  292             inferrer.types.allocatedLists[mask.allocationNode]; | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  334    * Check whether element is the parameter of a list adding method. |  331    * Check whether element is the parameter of a list adding method. | 
|  335    * The definition of what a list adding method is has to stay in sync with |  332    * The definition of what a list adding method is has to stay in sync with | 
|  336    * [isAddedToContainer]. |  333    * [isAddedToContainer]. | 
|  337    */ |  334    */ | 
|  338   bool isParameterOfListAddingMethod(Element element) { |  335   bool isParameterOfListAddingMethod(Element element) { | 
|  339     if (!element.isParameter) return false; |  336     if (!element.isParameter) return false; | 
|  340     if (element.enclosingClass != compiler.backend.listImplementation) { |  337     if (element.enclosingClass != compiler.backend.listImplementation) { | 
|  341       return false; |  338       return false; | 
|  342     } |  339     } | 
|  343     Element method = element.enclosingElement; |  340     Element method = element.enclosingElement; | 
|  344     return (method.name == '[]=') |  341     return (method.name == '[]=') || | 
|  345         || (method.name == 'add') |  342         (method.name == 'add') || | 
|  346         || (method.name == 'insert'); |  343         (method.name == 'insert'); | 
|  347   } |  344   } | 
|  348  |  345  | 
|  349   /** |  346   /** | 
|  350    * Check whether element is the parameter of a list adding method. |  347    * Check whether element is the parameter of a list adding method. | 
|  351    * The definition of what a list adding method is has to stay in sync with |  348    * The definition of what a list adding method is has to stay in sync with | 
|  352    * [isValueAddedToMap] and [isKeyAddedToMap]. |  349    * [isValueAddedToMap] and [isKeyAddedToMap]. | 
|  353    */ |  350    */ | 
|  354   bool isParameterOfMapAddingMethod(Element element) { |  351   bool isParameterOfMapAddingMethod(Element element) { | 
|  355     if (!element.isParameter) return false; |  352     if (!element.isParameter) return false; | 
|  356     if (element.enclosingClass != compiler.backend.mapImplementation) { |  353     if (element.enclosingClass != compiler.backend.mapImplementation) { | 
|  357       return false; |  354       return false; | 
|  358     } |  355     } | 
|  359     Element method = element.enclosingElement; |  356     Element method = element.enclosingElement; | 
|  360     return (method.name == '[]='); |  357     return (method.name == '[]='); | 
|  361   } |  358   } | 
|  362  |  359  | 
|  363   bool isClosure(Element element) { |  360   bool isClosure(Element element) { | 
|  364     if (!element.isFunction) return false; |  361     if (!element.isFunction) return false; | 
 |  362  | 
|  365     /// Creating an instance of a class that implements [Function] also |  363     /// Creating an instance of a class that implements [Function] also | 
|  366     /// closurizes the corresponding [call] member. We do not currently |  364     /// closurizes the corresponding [call] member. We do not currently | 
|  367     /// track these, thus the check for [isClosurized] on such a method will |  365     /// track these, thus the check for [isClosurized] on such a method will | 
|  368     /// return false. Instead we catch that case here for now. |  366     /// return false. Instead we catch that case here for now. | 
|  369     // TODO(herhut): Handle creation of closures from instances of Function. |  367     // TODO(herhut): Handle creation of closures from instances of Function. | 
|  370     if (element.isInstanceMember && |  368     if (element.isInstanceMember && element.name == Identifiers.call) { | 
|  371         element.name == Identifiers.call) { |  | 
|  372       return true; |  369       return true; | 
|  373     } |  370     } | 
|  374     Element outermost = element.outermostEnclosingMemberOrTopLevel; |  371     Element outermost = element.outermostEnclosingMemberOrTopLevel; | 
|  375     return outermost.declaration != element.declaration; |  372     return outermost.declaration != element.declaration; | 
|  376   } |  373   } | 
|  377  |  374  | 
|  378   void visitMemberTypeInformation(MemberTypeInformation info) { |  375   void visitMemberTypeInformation(MemberTypeInformation info) { | 
|  379     if (info.isClosurized) { |  376     if (info.isClosurized) { | 
|  380       bailout('Returned from a closurized method'); |  377       bailout('Returned from a closurized method'); | 
|  381     } |  378     } | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
|  400     } |  397     } | 
|  401     if (isParameterOfListAddingMethod(info.element) || |  398     if (isParameterOfListAddingMethod(info.element) || | 
|  402         isParameterOfMapAddingMethod(info.element)) { |  399         isParameterOfMapAddingMethod(info.element)) { | 
|  403       // These elements are being handled in |  400       // These elements are being handled in | 
|  404       // [visitDynamicCallSiteTypeInformation]. |  401       // [visitDynamicCallSiteTypeInformation]. | 
|  405       return; |  402       return; | 
|  406     } |  403     } | 
|  407     addNewEscapeInformation(info); |  404     addNewEscapeInformation(info); | 
|  408   } |  405   } | 
|  409 } |  406 } | 
| OLD | NEW |