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 part of type_graph_inferrer; | 5 part of type_graph_inferrer; |
6 | 6 |
7 // A set of selectors we know do not escape the elements inside the | 7 // A set of selectors we know do not escape the elements inside the |
8 // list. | 8 // list. |
9 Set<String> doesNotEscapeListSet = new Set<String>.from( | 9 Set<String> doesNotEscapeListSet = new Set<String>.from( |
10 const <String>[ | 10 const <String>[ |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
53 'hashCode', | 53 'hashCode', |
54 'toString', | 54 'toString', |
55 'noSuchMethod', | 55 'noSuchMethod', |
56 'runtimeType', | 56 'runtimeType', |
57 // from Map. | 57 // from Map. |
58 'isEmpty', | 58 'isEmpty', |
59 'isNotEmpty', | 59 'isNotEmpty', |
60 'length', | 60 'length', |
61 'clear', | 61 'clear', |
62 'containsKey', | 62 'containsKey', |
63 'containsValue' | 63 'containsValue', |
64 // [keys] only allows key values to escape, which we do not track. | |
65 'keys' | |
64 ]); | 66 ]); |
65 | 67 |
66 abstract class TracerVisitor implements TypeInformationVisitor { | 68 abstract class TracerVisitor implements TypeInformationVisitor { |
67 final TypeInformation tracedType; | 69 final TypeInformation tracedType; |
68 final TypeGraphInferrerEngine inferrer; | 70 final TypeGraphInferrerEngine inferrer; |
69 final Compiler compiler; | 71 final Compiler compiler; |
70 | 72 |
71 static const int MAX_ANALYSIS_COUNT = 16; | 73 static const int MAX_ANALYSIS_COUNT = 16; |
72 final Setlet<Element> analyzedElements = new Setlet<Element>(); | 74 final Setlet<Element> analyzedElements = new Setlet<Element>(); |
73 | 75 |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
206 if (user.selector.isIndex()) { | 208 if (user.selector.isIndex()) { |
207 addNewEscapeInformation(user); | 209 addNewEscapeInformation(user); |
208 } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { | 210 } else if (!doesNotEscapeMapSet.contains(user.selector.name)) { |
209 bailout('Escape from a map via [${user.selector.name}]'); | 211 bailout('Escape from a map via [${user.selector.name}]'); |
210 } | 212 } |
211 }); | 213 }); |
212 }); | 214 }); |
213 } | 215 } |
214 } | 216 } |
215 | 217 |
218 /* | |
219 * Checks whether this is a call to a list adding method. The definition | |
220 * of what list adding means has to stay in sync with | |
221 * [isParameterOfListAddingMethod]. | |
222 */ | |
216 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { | 223 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |
217 if (info.arguments == null) return false; | 224 if (info.arguments == null) return false; |
218 var receiverType = info.receiver.type; | 225 var receiverType = info.receiver.type; |
219 if (!receiverType.isContainer) return false; | 226 if (!receiverType.isContainer) return false; |
220 String selectorName = info.selector.name; | 227 String selectorName = info.selector.name; |
221 List<TypeInformation> arguments = info.arguments.positional; | 228 List<TypeInformation> arguments = info.arguments.positional; |
222 return (selectorName == '[]=' && currentUser == arguments[1]) | 229 return (selectorName == '[]=' && currentUser == arguments[1]) |
223 || (selectorName == 'insert' && currentUser == arguments[0]) | 230 || (selectorName == 'insert' && currentUser == arguments[0]) |
224 || (selectorName == 'add' && currentUser == arguments[0]); | 231 || (selectorName == 'add' && currentUser == arguments[0]); |
225 } | 232 } |
226 | 233 |
227 bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { | 234 bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) { |
228 if (info.arguments == null) return false; | 235 if (info.arguments == null) return false; |
229 var receiverType = info.receiver.type; | 236 var receiverType = info.receiver.type; |
230 if (!receiverType.isMap) return false; | 237 if (!receiverType.isMap) return false; |
231 String selectorName = info.selector.name; | 238 String selectorName = info.selector.name; |
232 List<TypeInformation> arguments = info.arguments.positional; | 239 List<TypeInformation> arguments = info.arguments.positional; |
233 return selectorName == '[]=' && currentUser == arguments[1]; | 240 return selectorName == '[]='; |
241 } | |
242 | |
243 /* Checks whether this is a call to a map adding method for values. The | |
karlklose
2014/03/31 14:39:27
Line break after '/*'. Consider using '///' instea
herhut
2014/04/02 13:25:53
Done.
| |
244 * definition of map adding method has to stay in sync with | |
245 * [isParameterOfMapAddingMethod]. | |
246 */ | |
247 bool isValueAddedToMap(DynamicCallSiteTypeInformation info) { | |
248 return isIndexSetOnMap(info) && | |
249 currentUser == info.arguments.positional[1]; | |
250 } | |
251 | |
252 /* Checks whether this is a call to a map adding method for keys. The | |
253 * definition of map adding method has to stay in sync with | |
254 * [isParameterOfMapAddingMethod]. | |
255 */ | |
256 bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) { | |
257 return isIndexSetOnMap(info) && | |
258 currentUser == info.arguments.positional[0]; | |
234 } | 259 } |
235 | 260 |
236 void visitDynamicCallSiteTypeInformation( | 261 void visitDynamicCallSiteTypeInformation( |
237 DynamicCallSiteTypeInformation info) { | 262 DynamicCallSiteTypeInformation info) { |
238 if (isAddedToContainer(info)) { | 263 if (isAddedToContainer(info)) { |
239 ContainerTypeMask mask = info.receiver.type; | 264 ContainerTypeMask mask = info.receiver.type; |
240 | 265 |
241 if (mask.allocationNode != null) { | 266 if (mask.allocationNode != null) { |
242 ListTypeInformation list = | 267 ListTypeInformation list = |
243 inferrer.types.allocatedLists[mask.allocationNode]; | 268 inferrer.types.allocatedLists[mask.allocationNode]; |
244 listsToAnalyze.add(list); | 269 listsToAnalyze.add(list); |
245 } else { | 270 } else { |
246 // The [ContainerTypeMask] is a union of two containers, and | 271 // The [ContainerTypeMask] is a union of two containers, and |
247 // we lose track of where these containers have been allocated | 272 // we lose track of where these containers have been allocated |
248 // at this point. | 273 // at this point. |
249 bailout('Stored in too many containers'); | 274 bailout('Stored in too many containers'); |
250 } | 275 } |
251 } else if (isValueAddedToMap(info)) { | 276 } else if (isValueAddedToMap(info)) { |
252 MapTypeMask mask = info.receiver.type; | 277 MapTypeMask mask = info.receiver.type; |
253 if (mask.allocationNode != null) { | 278 if (mask.allocationNode != null) { |
254 MapTypeInformation map = | 279 MapTypeInformation map = |
255 inferrer.types.allocatedMaps[mask.allocationNode]; | 280 inferrer.types.allocatedMaps[mask.allocationNode]; |
256 mapsToAnalyze.add(map); | 281 mapsToAnalyze.add(map); |
257 } else { | 282 } else { |
258 // The [MapTypeMask] is a union. See comment for | 283 // The [MapTypeMask] is a union. See comment for |
259 // [ContainerTypeMask] above. | 284 // [ContainerTypeMask] above. |
260 bailout('Stored in too many maps'); | 285 bailout('Stored in too many maps'); |
261 } | 286 } |
287 } else if (isKeyAddedToMap(info)) { | |
288 // We do not track the use of keys from a map, so we have to bail. | |
289 bailout('Used as key in Map'); | |
262 } | 290 } |
263 | 291 |
264 Iterable<Element> inferredTargetTypes = info.targets.map((element) { | 292 Iterable<Element> inferredTargetTypes = info.targets.map((element) { |
265 return inferrer.types.getInferredTypeOf(element); | 293 return inferrer.types.getInferredTypeOf(element); |
266 }); | 294 }); |
267 if (inferredTargetTypes.any((user) => user == currentUser)) { | 295 if (inferredTargetTypes.any((user) => user == currentUser)) { |
268 addNewEscapeInformation(info); | 296 addNewEscapeInformation(info); |
269 } | 297 } |
270 } | 298 } |
271 | 299 |
300 /* | |
301 * Check whether element is the parameter of a list adding method. | |
302 * The definition of what a list adding method is has to stay in sync with | |
303 * [isAddedToContainer]. | |
304 */ | |
272 bool isParameterOfListAddingMethod(Element element) { | 305 bool isParameterOfListAddingMethod(Element element) { |
273 if (!element.isParameter()) return false; | 306 if (!element.isParameter()) return false; |
274 if (element.getEnclosingClass() != compiler.backend.listImplementation) { | 307 if (element.getEnclosingClass() != compiler.backend.listImplementation) { |
275 return false; | 308 return false; |
276 } | 309 } |
277 Element method = element.enclosingElement; | 310 Element method = element.enclosingElement; |
278 return (method.name == '[]=') | 311 return (method.name == '[]=') |
279 || (method.name == 'add') | 312 || (method.name == 'add') |
280 || (method.name == 'insert'); | 313 || (method.name == 'insert'); |
281 } | 314 } |
282 | 315 |
316 /* | |
317 * Check whether element is the parameter of a list adding method. | |
318 * The definition of what a list adding method is has to stay in sync with | |
319 * [isValueAddedToMap] and [isKeyAddedToMap]. | |
320 */ | |
321 bool isParameterOfMapAddingMethod(Element element) { | |
322 if (!element.isParameter()) return false; | |
323 if (element.getEnclosingClass() != compiler.backend.mapImplementation) { | |
324 return false; | |
325 } | |
326 Element method = element.enclosingElement; | |
327 return (method.name == '[]='); | |
328 } | |
329 | |
283 bool isClosure(Element element) { | 330 bool isClosure(Element element) { |
284 if (!element.isFunction()) return false; | 331 if (!element.isFunction()) return false; |
285 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); | 332 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
286 return outermost.declaration != element.declaration; | 333 return outermost.declaration != element.declaration; |
287 } | 334 } |
288 | 335 |
289 void visitElementTypeInformation(ElementTypeInformation info) { | 336 void visitElementTypeInformation(ElementTypeInformation info) { |
290 Element element = info.element; | 337 Element element = info.element; |
291 if (element.isParameter() | 338 if (element.isParameter() |
292 && inferrer.isNativeElement(element.enclosingElement)) { | 339 && inferrer.isNativeElement(element.enclosingElement)) { |
293 bailout('Passed to a native method'); | 340 bailout('Passed to a native method'); |
294 } | 341 } |
295 if (info.isClosurized()) { | 342 if (info.isClosurized()) { |
296 bailout('Returned from a closurized method'); | 343 bailout('Returned from a closurized method'); |
297 } | 344 } |
298 if (isClosure(info.element)) { | 345 if (isClosure(info.element)) { |
299 bailout('Returned from a closure'); | 346 bailout('Returned from a closure'); |
300 } | 347 } |
301 if (compiler.backend.isNeededForReflection(info.element)) { | 348 if (compiler.backend.isNeededForReflection(info.element)) { |
302 bailout('Escape in reflection'); | 349 bailout('Escape in reflection'); |
303 } | 350 } |
304 if (isParameterOfListAddingMethod(info.element)) { | 351 if (isParameterOfListAddingMethod(info.element) || |
352 isParameterOfMapAddingMethod(info.element)) { | |
305 // These elements are being handled in | 353 // These elements are being handled in |
306 // [visitDynamicCallSiteTypeInformation]. | 354 // [visitDynamicCallSiteTypeInformation]. |
307 return; | 355 return; |
308 } | 356 } |
309 addNewEscapeInformation(info); | 357 addNewEscapeInformation(info); |
310 } | 358 } |
311 } | 359 } |
OLD | NEW |