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 |