Chromium Code Reviews| 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 /** | 7 /** |
| 8 * A set of selector names that [List] implements, that we know do not | 8 * A set of selector names that [List] implements, that we know do not |
| 9 * change the element type of the list, or let the list escape to code | 9 * change the element type of the list, or let the list escape to code |
| 10 * that might change the element type. | 10 * that might change the element type. |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 118 'lastIndexOf', | 118 'lastIndexOf', |
| 119 'sublist', | 119 'sublist', |
| 120 'getRange', | 120 'getRange', |
| 121 'asMap', | 121 'asMap', |
| 122 | 122 |
| 123 // From JSArray. | 123 // From JSArray. |
| 124 'checkMutable', | 124 'checkMutable', |
| 125 'checkGrowable', | 125 'checkGrowable', |
| 126 ]); | 126 ]); |
| 127 | 127 |
| 128 // A set of selectors we know do not escape the elements inside the | |
| 129 // list. | |
| 130 Set<String> doesNotEscapeElementSet = new Set<String>.from( | |
|
kasperl
2013/12/06 11:29:57
Would it be possible to maintain this as annotatio
ngeoffray
2013/12/06 11:36:26
For some yes, but how would you do it for methods
| |
| 131 const <String>[ | |
| 132 // From Object. | |
| 133 '==', | |
| 134 'hashCode', | |
| 135 'toString', | |
| 136 'noSuchMethod', | |
| 137 'runtimeType', | |
| 138 | |
| 139 // From Iterable. | |
| 140 'isEmpty', | |
| 141 'isNotEmpty', | |
| 142 'length', | |
| 143 'any', | |
| 144 'contains', | |
| 145 'every', | |
| 146 'join', | |
| 147 | |
| 148 // From List. | |
| 149 'add', | |
| 150 'addAll', | |
| 151 'clear', | |
| 152 'fillRange', | |
| 153 'indexOf', | |
| 154 'insert', | |
| 155 'insertAll', | |
| 156 'lastIndexOf', | |
| 157 'remove', | |
| 158 'removeRange', | |
| 159 'replaceRange', | |
| 160 'setAll', | |
| 161 'setRange', | |
| 162 'shuffle', | |
| 163 '[]=', | |
| 164 | |
| 165 // From JSArray. | |
| 166 'checkMutable', | |
| 167 'checkGrowable', | |
| 168 ]); | |
| 169 | |
| 128 bool _VERBOSE = false; | 170 bool _VERBOSE = false; |
| 129 | 171 |
| 130 class ContainerTracerVisitor implements TypeInformationVisitor { | 172 class ContainerTracerVisitor implements TypeInformationVisitor { |
| 131 final ContainerTypeInformation container; | 173 final ContainerTypeInformation container; |
| 132 final TypeGraphInferrerEngine inferrer; | 174 final TypeGraphInferrerEngine inferrer; |
| 133 final Compiler compiler; | 175 final Compiler compiler; |
| 134 | 176 |
| 135 // The set of [TypeInformation] where the traced container could | |
| 136 // flow in, and operations done on them. | |
| 137 final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); | |
| 138 | 177 |
| 139 // Work list that gets populated with [TypeInformation] that could | 178 // Work list that gets populated with [TypeInformation] that could |
| 140 // contain the container. | 179 // contain the container. |
| 141 final List<TypeInformation> workList = <TypeInformation>[]; | 180 final List<TypeInformation> workList = <TypeInformation>[]; |
| 142 | 181 |
| 182 // Work list of containers to analyze after analyzing the users of a | |
| 183 // [TypeInformation] that may be [container]. We know [container] | |
| 184 // has been stored in these containers and we must check how | |
| 185 // [container] escapes from these containers. | |
| 186 final List<ContainerTypeInformation> containersToAnalyze = | |
| 187 <ContainerTypeInformation>[]; | |
| 188 | |
| 143 // The current [TypeInformation] in the analysis. | 189 // The current [TypeInformation] in the analysis. |
| 144 TypeInformation currentUser; | 190 TypeInformation currentUser; |
| 145 | 191 |
| 146 // The list of found assignments to the container. | 192 // The list of found assignments to the container. |
| 147 final List<TypeInformation> assignments = <TypeInformation>[]; | 193 final List<TypeInformation> assignments = <TypeInformation>[]; |
| 148 | 194 |
| 149 bool callsGrowableMethod = false; | 195 bool callsGrowableMethod = false; |
| 150 bool continueAnalyzing = true; | 196 bool continueAnalyzing = true; |
| 151 | 197 |
| 152 static const int MAX_ANALYSIS_COUNT = 16; | 198 static const int MAX_ANALYSIS_COUNT = 16; |
| 153 final Setlet<Element> analyzedElements = new Setlet<Element>(); | 199 final Setlet<Element> analyzedElements = new Setlet<Element>(); |
| 154 | 200 |
| 155 ContainerTracerVisitor(this.container, inferrer) | 201 ContainerTracerVisitor(this.container, inferrer) |
| 156 : this.inferrer = inferrer, this.compiler = inferrer.compiler; | 202 : this.inferrer = inferrer, this.compiler = inferrer.compiler; |
| 157 | 203 |
| 158 void addNewEscapeInformation(TypeInformation info) { | 204 void addNewEscapeInformation(TypeInformation info) { |
| 159 if (flowsInto.contains(info)) return; | 205 if (container.flowsInto.contains(info)) return; |
| 160 flowsInto.add(info); | 206 container.flowsInto.add(info); |
| 161 workList.add(info); | 207 workList.add(info); |
| 162 } | 208 } |
| 163 | 209 |
| 164 List<TypeInformation> run() { | 210 List<TypeInformation> run() { |
| 165 // Collect the [TypeInformation] where the container can flow in, | 211 // Collect the [TypeInformation] where the container can flow in, |
| 166 // as well as the operations done on all these [TypeInformation]s. | 212 // as well as the operations done on all these [TypeInformation]s. |
| 167 addNewEscapeInformation(container); | 213 addNewEscapeInformation(container); |
| 168 while (!workList.isEmpty) { | 214 while (!workList.isEmpty) { |
| 169 currentUser = workList.removeLast(); | 215 currentUser = workList.removeLast(); |
| 170 currentUser.users.forEach((TypeInformation info) { | 216 currentUser.users.forEach((TypeInformation info) { |
| 171 analyzedElements.add(info.owner); | 217 analyzedElements.add(info.owner); |
| 172 info.accept(this); | 218 info.accept(this); |
| 173 }); | 219 }); |
| 220 while (!containersToAnalyze.isEmpty) { | |
| 221 analyzeStoredIntoContainer(containersToAnalyze.removeLast()); | |
| 222 } | |
| 174 if (!continueAnalyzing) break; | 223 if (!continueAnalyzing) break; |
| 175 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { | 224 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { |
| 176 bailout('Too many users'); | 225 bailout('Too many users'); |
| 177 break; | 226 break; |
| 178 } | 227 } |
| 179 } | 228 } |
| 180 | 229 |
| 181 if (continueAnalyzing) { | 230 if (continueAnalyzing) { |
| 182 if (!callsGrowableMethod && container.inferredLength == null) { | 231 if (!callsGrowableMethod && container.inferredLength == null) { |
| 183 container.inferredLength = container.originalLength; | 232 container.inferredLength = container.originalLength; |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 204 visitPhiElementTypeInformation(PhiElementTypeInformation info) { | 253 visitPhiElementTypeInformation(PhiElementTypeInformation info) { |
| 205 addNewEscapeInformation(info); | 254 addNewEscapeInformation(info); |
| 206 } | 255 } |
| 207 | 256 |
| 208 visitElementInContainerTypeInformation( | 257 visitElementInContainerTypeInformation( |
| 209 ElementInContainerTypeInformation info) { | 258 ElementInContainerTypeInformation info) { |
| 210 addNewEscapeInformation(info); | 259 addNewEscapeInformation(info); |
| 211 } | 260 } |
| 212 | 261 |
| 213 visitContainerTypeInformation(ContainerTypeInformation info) { | 262 visitContainerTypeInformation(ContainerTypeInformation info) { |
| 214 if (container != info) { | 263 containersToAnalyze.add(info); |
| 215 bailout('Stored in a container'); | |
| 216 } | |
| 217 } | 264 } |
| 218 | 265 |
| 219 visitConcreteTypeInformation(ConcreteTypeInformation info) {} | 266 visitConcreteTypeInformation(ConcreteTypeInformation info) {} |
| 220 | 267 |
| 221 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | 268 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| 222 bailout('Passed to a closure'); | 269 bailout('Passed to a closure'); |
| 223 } | 270 } |
| 224 | 271 |
| 225 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 272 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 226 Element called = info.calledElement; | 273 Element called = info.calledElement; |
| 227 if (called.isForeign(compiler) && called.name == 'JS') { | 274 if (called.isForeign(compiler) && called.name == 'JS') { |
| 228 bailout('Used in JS ${info.call}'); | 275 bailout('Used in JS ${info.call}'); |
| 229 } | 276 } |
| 230 if (inferrer.types.getInferredTypeOf(called) == currentUser) { | 277 if (inferrer.types.getInferredTypeOf(called) == currentUser) { |
| 231 addNewEscapeInformation(info); | 278 addNewEscapeInformation(info); |
| 232 } | 279 } |
| 233 } | 280 } |
| 234 | 281 |
| 282 void analyzeStoredIntoContainer(ContainerTypeInformation container) { | |
| 283 inferrer.analyzeContainer(container); | |
| 284 if (container.bailedOut) { | |
| 285 bailout('Stored in a container that bailed out'); | |
| 286 } else { | |
| 287 container.flowsInto.forEach((flow) { | |
| 288 flow.users.forEach((user) { | |
| 289 if (user is !DynamicCallSiteTypeInformation) return; | |
| 290 if (user.receiver != flow) return; | |
| 291 if (returnsElementTypeSet.contains(user.selector)) { | |
| 292 addNewEscapeInformation(user); | |
| 293 } else if (!doesNotEscapeElementSet.contains(user.selector.name)) { | |
| 294 bailout('Escape from a container'); | |
| 295 } | |
| 296 }); | |
| 297 }); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { | |
| 302 var receiverType = info.receiver.type; | |
| 303 if (!receiverType.isContainer) return false; | |
| 304 String selectorName = info.selector.name; | |
| 305 List<TypeInformation> arguments = info.arguments.positional; | |
| 306 return (selectorName == '[]=' && currentUser == arguments[1]) | |
| 307 || (selectorName == 'insert' && currentUser == arguments[0]) | |
| 308 || (selectorName == 'add' && currentUser == arguments[0]); | |
| 309 } | |
| 310 | |
| 235 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | 311 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| 236 Selector selector = info.selector; | 312 Selector selector = info.selector; |
| 237 String selectorName = selector.name; | 313 String selectorName = selector.name; |
| 238 if (currentUser == info.receiver) { | 314 if (currentUser == info.receiver) { |
| 239 if (!okSelectorsSet.contains(selectorName)) { | 315 if (!okSelectorsSet.contains(selectorName)) { |
| 240 if (selector.isCall()) { | 316 if (selector.isCall()) { |
| 241 int positionalLength = info.arguments.positional.length; | 317 int positionalLength = info.arguments.positional.length; |
| 242 if (selectorName == 'add') { | 318 if (selectorName == 'add') { |
| 243 if (positionalLength == 1) { | 319 if (positionalLength == 1) { |
| 244 assignments.add(info.arguments.positional[0]); | 320 assignments.add(info.arguments.positional[0]); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 262 callsGrowableMethod = true; | 338 callsGrowableMethod = true; |
| 263 } | 339 } |
| 264 if (selectorName == 'length' && selector.isSetter()) { | 340 if (selectorName == 'length' && selector.isSetter()) { |
| 265 callsGrowableMethod = true; | 341 callsGrowableMethod = true; |
| 266 assignments.add(inferrer.types.nullType); | 342 assignments.add(inferrer.types.nullType); |
| 267 } | 343 } |
| 268 } else if (selector.isCall() | 344 } else if (selector.isCall() |
| 269 && !info.targets.every((element) => element.isFunction())) { | 345 && !info.targets.every((element) => element.isFunction())) { |
| 270 bailout('Passed to a closure'); | 346 bailout('Passed to a closure'); |
| 271 return; | 347 return; |
| 348 } else if (isAddedToContainer(info)) { | |
| 349 ContainerTypeMask mask = info.receiver.type; | |
| 350 if (mask.allocationNode != null) { | |
| 351 ContainerTypeInformation container = | |
| 352 inferrer.types.allocatedContainers[mask.allocationNode]; | |
| 353 containersToAnalyze.add(container); | |
| 354 } else { | |
| 355 bailout('Stored in too many containers'); | |
|
kasperl
2013/12/06 11:29:57
Add a comment that explains why a null allocationN
ngeoffray
2013/12/06 11:36:26
Done.
| |
| 356 } | |
| 272 } | 357 } |
| 273 | 358 |
| 274 if (info.targets | 359 if (info.targets |
| 275 .map((element) => inferrer.types.getInferredTypeOf(element)) | 360 .map((element) => inferrer.types.getInferredTypeOf(element)) |
| 276 .any((other) => other == currentUser)) { | 361 .any((other) => other == currentUser)) { |
| 277 addNewEscapeInformation(info); | 362 addNewEscapeInformation(info); |
| 278 } | 363 } |
| 279 | |
| 280 } | 364 } |
| 281 | 365 |
| 282 bool isClosure(Element element) { | 366 bool isClosure(Element element) { |
| 283 if (!element.isFunction()) return false; | 367 if (!element.isFunction()) return false; |
| 284 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); | 368 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
| 285 return outermost.declaration != element.declaration; | 369 return outermost.declaration != element.declaration; |
| 286 } | 370 } |
| 287 | 371 |
| 372 bool isParameterOfListAddingMethod(Element element) { | |
| 373 if (!element.isParameter()) return false; | |
| 374 if (element.getEnclosingClass() != compiler.backend.listImplementation) { | |
| 375 return false; | |
| 376 } | |
| 377 Element method = element.enclosingElement; | |
| 378 return (method.name == '[]=') | |
| 379 || (method.name == 'add') | |
| 380 || (method.name == 'insert'); | |
| 381 } | |
| 382 | |
| 288 visitElementTypeInformation(ElementTypeInformation info) { | 383 visitElementTypeInformation(ElementTypeInformation info) { |
| 289 if (isClosure(info.element)) { | 384 if (isClosure(info.element)) { |
| 290 bailout('Returned from a closure'); | 385 bailout('Returned from a closure'); |
| 291 } | 386 } |
| 292 if (compiler.backend.isNeededForReflection(info.element)) { | 387 if (compiler.backend.isNeededForReflection(info.element)) { |
| 293 bailout('Escape in reflection'); | 388 bailout('Escape in reflection'); |
| 294 } | 389 } |
| 390 if (isParameterOfListAddingMethod(info.element)) { | |
| 391 // These elements are being handled in | |
| 392 // [visitDynamicCallSiteTypeInformation]. | |
| 393 return; | |
| 394 } | |
| 295 addNewEscapeInformation(info); | 395 addNewEscapeInformation(info); |
| 296 } | 396 } |
| 297 } | 397 } |
| OLD | NEW |