| 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 |