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 inferrer_visitor; | 5 library inferrer_visitor; |
6 | 6 |
7 import 'dart:collection' show | 7 import 'dart:collection' show IterableMixin; |
8 IterableMixin; | |
9 | 8 |
10 import '../common.dart'; | 9 import '../common.dart'; |
11 import '../compiler.dart' show | 10 import '../compiler.dart' show Compiler; |
12 Compiler; | |
13 import '../constants/constant_system.dart'; | 11 import '../constants/constant_system.dart'; |
14 import '../constants/expressions.dart'; | 12 import '../constants/expressions.dart'; |
15 import '../dart_types.dart'; | 13 import '../dart_types.dart'; |
16 import '../elements/elements.dart'; | 14 import '../elements/elements.dart'; |
17 import '../resolution/operators.dart'; | 15 import '../resolution/operators.dart'; |
18 import '../resolution/semantic_visitor.dart'; | 16 import '../resolution/semantic_visitor.dart'; |
19 import '../resolution/tree_elements.dart' show | 17 import '../resolution/tree_elements.dart' show TreeElements; |
20 TreeElements; | |
21 import '../tree/tree.dart'; | 18 import '../tree/tree.dart'; |
22 import '../types/types.dart' show | 19 import '../types/types.dart' show TypeMask; |
23 TypeMask; | 20 import '../types/constants.dart' show computeTypeMask; |
24 import '../types/constants.dart' show | 21 import '../universe/call_structure.dart' show CallStructure; |
25 computeTypeMask; | 22 import '../universe/selector.dart' show Selector; |
26 import '../universe/call_structure.dart' show | |
27 CallStructure; | |
28 import '../universe/selector.dart' show | |
29 Selector; | |
30 import '../util/util.dart'; | 23 import '../util/util.dart'; |
31 import '../world.dart' show | 24 import '../world.dart' show ClassWorld; |
32 ClassWorld; | |
33 | 25 |
34 /** | 26 /** |
35 * The interface [InferrerVisitor] will use when working on types. | 27 * The interface [InferrerVisitor] will use when working on types. |
36 */ | 28 */ |
37 abstract class TypeSystem<T> { | 29 abstract class TypeSystem<T> { |
38 T get dynamicType; | 30 T get dynamicType; |
39 T get nullType; | 31 T get nullType; |
40 T get intType; | 32 T get intType; |
41 T get uint31Type; | 33 T get uint31Type; |
42 T get uint32Type; | 34 T get uint32Type; |
(...skipping 17 matching lines...) Expand all Loading... |
60 T stringLiteralType(DartString value); | 52 T stringLiteralType(DartString value); |
61 T boolLiteralType(LiteralBool value); | 53 T boolLiteralType(LiteralBool value); |
62 | 54 |
63 T nonNullSubtype(ClassElement type); | 55 T nonNullSubtype(ClassElement type); |
64 T nonNullSubclass(ClassElement type); | 56 T nonNullSubclass(ClassElement type); |
65 T nonNullExact(ClassElement type); | 57 T nonNullExact(ClassElement type); |
66 T nonNullEmpty(); | 58 T nonNullEmpty(); |
67 bool isNull(T type); | 59 bool isNull(T type); |
68 TypeMask newTypedSelector(T receiver, TypeMask mask); | 60 TypeMask newTypedSelector(T receiver, TypeMask mask); |
69 | 61 |
70 T allocateList(T type, | 62 T allocateList(T type, Node node, Element enclosing, |
71 Node node, | 63 [T elementType, int length]); |
72 Element enclosing, | |
73 [T elementType, int length]); | |
74 | 64 |
75 T allocateMap(T type, Node node, Element element, [List<T> keyType, | 65 T allocateMap(T type, Node node, Element element, |
76 List<T> valueType]); | 66 [List<T> keyType, List<T> valueType]); |
77 | 67 |
78 T allocateClosure(Node node, Element element); | 68 T allocateClosure(Node node, Element element); |
79 | 69 |
80 /** | 70 /** |
81 * Returns the least upper bound between [firstType] and | 71 * Returns the least upper bound between [firstType] and |
82 * [secondType]. | 72 * [secondType]. |
83 */ | 73 */ |
84 T computeLUB(T firstType, T secondType); | 74 T computeLUB(T firstType, T secondType); |
85 | 75 |
86 /** | 76 /** |
(...skipping 12 matching lines...) Expand all Loading... |
99 * Returns a new type that unions [firstInput] and [secondInput]. | 89 * Returns a new type that unions [firstInput] and [secondInput]. |
100 */ | 90 */ |
101 T allocateDiamondPhi(T firstInput, T secondInput); | 91 T allocateDiamondPhi(T firstInput, T secondInput); |
102 | 92 |
103 /** | 93 /** |
104 * Returns a new type for holding the potential types of [element]. | 94 * Returns a new type for holding the potential types of [element]. |
105 * [inputType] is the first incoming type of the phi. | 95 * [inputType] is the first incoming type of the phi. |
106 */ | 96 */ |
107 T allocatePhi(Node node, Local variable, T inputType); | 97 T allocatePhi(Node node, Local variable, T inputType); |
108 | 98 |
109 | |
110 /** | 99 /** |
111 * Returns a new type for holding the potential types of [element]. | 100 * Returns a new type for holding the potential types of [element]. |
112 * [inputType] is the first incoming type of the phi. [allocateLoopPhi] | 101 * [inputType] is the first incoming type of the phi. [allocateLoopPhi] |
113 * only differs from [allocatePhi] in that it allows the underlying | 102 * only differs from [allocatePhi] in that it allows the underlying |
114 * implementation of [TypeSystem] to differentiate Phi nodes due to loops | 103 * implementation of [TypeSystem] to differentiate Phi nodes due to loops |
115 * from other merging uses. | 104 * from other merging uses. |
116 */ | 105 */ |
117 T allocateLoopPhi(Node node, Local variable, T inputType); | 106 T allocateLoopPhi(Node node, Local variable, T inputType); |
118 | 107 |
119 /** | 108 /** |
(...skipping 16 matching lines...) Expand all Loading... |
136 bool selectorNeedsUpdate(T receiverType, TypeMask mask); | 125 bool selectorNeedsUpdate(T receiverType, TypeMask mask); |
137 | 126 |
138 /** | 127 /** |
139 * Returns a new receiver type for this [selector] applied to | 128 * Returns a new receiver type for this [selector] applied to |
140 * [receiverType]. | 129 * [receiverType]. |
141 * | 130 * |
142 * The option [isConditional] is true when [selector] was seen in a | 131 * The option [isConditional] is true when [selector] was seen in a |
143 * conditional send (e.g. `a?.selector`), in which case the returned type may | 132 * conditional send (e.g. `a?.selector`), in which case the returned type may |
144 * be null. | 133 * be null. |
145 */ | 134 */ |
146 T refineReceiver(Selector selector, | 135 T refineReceiver( |
147 TypeMask mask, | 136 Selector selector, TypeMask mask, T receiverType, bool isConditional); |
148 T receiverType, | |
149 bool isConditional); | |
150 | 137 |
151 /** | 138 /** |
152 * Returns the internal inferrer representation for [mask]. | 139 * Returns the internal inferrer representation for [mask]. |
153 */ | 140 */ |
154 T getConcreteTypeFor(TypeMask mask); | 141 T getConcreteTypeFor(TypeMask mask); |
155 } | 142 } |
156 | 143 |
157 /** | 144 /** |
158 * A variable scope holds types for variables. It has a link to a | 145 * A variable scope holds types for variables. It has a link to a |
159 * parent scope, but never changes the types in that parent. Instead, | 146 * parent scope, but never changes the types in that parent. Instead, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 variables = new Map<Local, T>(); | 191 variables = new Map<Local, T>(); |
205 } | 192 } |
206 variables[variable] = mask; | 193 variables[variable] = mask; |
207 } | 194 } |
208 | 195 |
209 void forEachOwnLocal(void f(Local variable, T type)) { | 196 void forEachOwnLocal(void f(Local variable, T type)) { |
210 if (variables == null) return; | 197 if (variables == null) return; |
211 variables.forEach(f); | 198 variables.forEach(f); |
212 } | 199 } |
213 | 200 |
214 void forEachLocalUntilNode(Node node, | 201 void forEachLocalUntilNode(Node node, void f(Local variable, T type), |
215 void f(Local variable, T type), | 202 [Setlet<Local> seenLocals]) { |
216 [Setlet<Local> seenLocals]) { | |
217 if (seenLocals == null) seenLocals = new Setlet<Local>(); | 203 if (seenLocals == null) seenLocals = new Setlet<Local>(); |
218 if (variables != null) { | 204 if (variables != null) { |
219 variables.forEach((variable, type) { | 205 variables.forEach((variable, type) { |
220 if (seenLocals.contains(variable)) return; | 206 if (seenLocals.contains(variable)) return; |
221 seenLocals.add(variable); | 207 seenLocals.add(variable); |
222 f(variable, type); | 208 f(variable, type); |
223 }); | 209 }); |
224 } | 210 } |
225 if (block == node) return; | 211 if (block == node) return; |
226 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); | 212 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 T readField(Element field) { | 252 T readField(Element field) { |
267 return fields == null ? null : fields[field]; | 253 return fields == null ? null : fields[field]; |
268 } | 254 } |
269 | 255 |
270 void forEach(void f(Element element, T type)) { | 256 void forEach(void f(Element element, T type)) { |
271 if (fields == null) return; | 257 if (fields == null) return; |
272 fields.forEach(f); | 258 fields.forEach(f); |
273 } | 259 } |
274 | 260 |
275 void mergeDiamondFlow(FieldInitializationScope<T> thenScope, | 261 void mergeDiamondFlow(FieldInitializationScope<T> thenScope, |
276 FieldInitializationScope<T> elseScope) { | 262 FieldInitializationScope<T> elseScope) { |
277 // Quick bailout check. If [isThisExposed] is true, we know the | 263 // Quick bailout check. If [isThisExposed] is true, we know the |
278 // code following won't do anything. | 264 // code following won't do anything. |
279 if (isThisExposed) return; | 265 if (isThisExposed) return; |
280 if (elseScope == null || elseScope.fields == null) { | 266 if (elseScope == null || elseScope.fields == null) { |
281 elseScope = this; | 267 elseScope = this; |
282 } | 268 } |
283 | 269 |
284 thenScope.forEach((Element field, T type) { | 270 thenScope.forEach((Element field, T type) { |
285 T otherType = elseScope.readField(field); | 271 T otherType = elseScope.readField(field); |
286 if (otherType == null) return; | 272 if (otherType == null) return; |
287 updateField(field, types.allocateDiamondPhi(type, otherType)); | 273 updateField(field, types.allocateDiamondPhi(type, otherType)); |
288 }); | 274 }); |
289 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; | 275 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; |
290 } | 276 } |
291 } | 277 } |
292 | 278 |
293 /** | 279 /** |
294 * Placeholder for inferred arguments types on sends. | 280 * Placeholder for inferred arguments types on sends. |
295 */ | 281 */ |
296 class ArgumentsTypes<T> extends IterableMixin<T> { | 282 class ArgumentsTypes<T> extends IterableMixin<T> { |
297 final List<T> positional; | 283 final List<T> positional; |
298 final Map<String, T> named; | 284 final Map<String, T> named; |
299 ArgumentsTypes(this.positional, named) | 285 ArgumentsTypes(this.positional, named) |
300 : this.named = (named == null || named.isEmpty) ? const {} : named { | 286 : this.named = (named == null || named.isEmpty) ? const {} : named { |
301 assert(this.positional.every((T type) => type != null)); | 287 assert(this.positional.every((T type) => type != null)); |
302 assert(this.named.values.every((T type) => type != null)); | 288 assert(this.named.values.every((T type) => type != null)); |
303 } | 289 } |
304 | 290 |
305 ArgumentsTypes.empty() : positional = const [], named = const {}; | 291 ArgumentsTypes.empty() |
| 292 : positional = const [], |
| 293 named = const {}; |
306 | 294 |
307 int get length => positional.length + named.length; | 295 int get length => positional.length + named.length; |
308 | 296 |
309 Iterator<T> get iterator => new ArgumentsTypesIterator(this); | 297 Iterator<T> get iterator => new ArgumentsTypesIterator(this); |
310 | 298 |
311 String toString() => "{ positional = $positional, named = $named }"; | 299 String toString() => "{ positional = $positional, named = $named }"; |
312 | 300 |
313 bool operator==(other) { | 301 bool operator ==(other) { |
314 if (positional.length != other.positional.length) return false; | 302 if (positional.length != other.positional.length) return false; |
315 if (named.length != other.named.length) return false; | 303 if (named.length != other.named.length) return false; |
316 for (int i = 0; i < positional.length; i++) { | 304 for (int i = 0; i < positional.length; i++) { |
317 if (positional[i] != other.positional[i]) return false; | 305 if (positional[i] != other.positional[i]) return false; |
318 } | 306 } |
319 named.forEach((name, type) { | 307 named.forEach((name, type) { |
320 if (other.named[name] != type) return false; | 308 if (other.named[name] != type) return false; |
321 }); | 309 }); |
322 return true; | 310 return true; |
323 } | 311 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 | 347 |
360 bool moveNext() { | 348 bool moveNext() { |
361 if (_iteratePositional && positional.moveNext()) { | 349 if (_iteratePositional && positional.moveNext()) { |
362 return true; | 350 return true; |
363 } | 351 } |
364 _iteratePositional = false; | 352 _iteratePositional = false; |
365 return named.moveNext(); | 353 return named.moveNext(); |
366 } | 354 } |
367 } | 355 } |
368 | 356 |
369 | |
370 abstract class MinimalInferrerEngine<T> { | 357 abstract class MinimalInferrerEngine<T> { |
371 /** | 358 /** |
372 * Returns the type of [element]. | 359 * Returns the type of [element]. |
373 */ | 360 */ |
374 T typeOfElement(Element element); | 361 T typeOfElement(Element element); |
375 | 362 |
376 /** | 363 /** |
377 * Records that [node] sets non-final field [element] to be of type | 364 * Records that [node] sets non-final field [element] to be of type |
378 * [type]. | 365 * [type]. |
379 */ | 366 */ |
(...skipping 21 matching lines...) Expand all Loading... |
401 final Map<Local, Element> captured; | 388 final Map<Local, Element> captured; |
402 final Map<Local, Element> capturedAndBoxed; | 389 final Map<Local, Element> capturedAndBoxed; |
403 final FieldInitializationScope<T> fieldScope; | 390 final FieldInitializationScope<T> fieldScope; |
404 LocalsHandler<T> tryBlock; | 391 LocalsHandler<T> tryBlock; |
405 bool seenReturnOrThrow = false; | 392 bool seenReturnOrThrow = false; |
406 bool seenBreakOrContinue = false; | 393 bool seenBreakOrContinue = false; |
407 | 394 |
408 bool get aborts { | 395 bool get aborts { |
409 return seenReturnOrThrow || seenBreakOrContinue; | 396 return seenReturnOrThrow || seenBreakOrContinue; |
410 } | 397 } |
| 398 |
411 bool get inTryBlock => tryBlock != null; | 399 bool get inTryBlock => tryBlock != null; |
412 | 400 |
413 LocalsHandler(this.inferrer, | 401 LocalsHandler(this.inferrer, this.types, this.compiler, Node block, |
414 this.types, | 402 [this.fieldScope]) |
415 this.compiler, | |
416 Node block, | |
417 [this.fieldScope]) | |
418 : locals = new VariableScope<T>(block), | 403 : locals = new VariableScope<T>(block), |
419 captured = new Map<Local, Element>(), | 404 captured = new Map<Local, Element>(), |
420 capturedAndBoxed = new Map<Local, Element>(), | 405 capturedAndBoxed = new Map<Local, Element>(), |
421 tryBlock = null; | 406 tryBlock = null; |
422 | 407 |
423 LocalsHandler.from(LocalsHandler<T> other, | 408 LocalsHandler.from(LocalsHandler<T> other, Node block, |
424 Node block, | 409 {bool useOtherTryBlock: true}) |
425 {bool useOtherTryBlock: true}) | |
426 : locals = new VariableScope<T>(block, other.locals), | 410 : locals = new VariableScope<T>(block, other.locals), |
427 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), | 411 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), |
428 captured = other.captured, | 412 captured = other.captured, |
429 capturedAndBoxed = other.capturedAndBoxed, | 413 capturedAndBoxed = other.capturedAndBoxed, |
430 types = other.types, | 414 types = other.types, |
431 inferrer = other.inferrer, | 415 inferrer = other.inferrer, |
432 compiler = other.compiler { | 416 compiler = other.compiler { |
433 tryBlock = useOtherTryBlock ? other.tryBlock : this; | 417 tryBlock = useOtherTryBlock ? other.tryBlock : this; |
434 } | 418 } |
435 | 419 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 } | 456 } |
473 updateLocal() { | 457 updateLocal() { |
474 T currentType = locals[local]; | 458 T currentType = locals[local]; |
475 | 459 |
476 SendSet send = node != null ? node.asSendSet() : null; | 460 SendSet send = node != null ? node.asSendSet() : null; |
477 if (send != null && send.isIfNullAssignment && currentType != null) { | 461 if (send != null && send.isIfNullAssignment && currentType != null) { |
478 // If-null assignments may return either the new or the original value | 462 // If-null assignments may return either the new or the original value |
479 // narrowed to non-null. | 463 // narrowed to non-null. |
480 type = types.addPhiInput( | 464 type = types.addPhiInput( |
481 local, | 465 local, |
482 types.allocatePhi(locals.block, local, | 466 types.allocatePhi( |
483 types.narrowNotNull(currentType)), | 467 locals.block, local, types.narrowNotNull(currentType)), |
484 type); | 468 type); |
485 } | 469 } |
486 locals[local] = type; | 470 locals[local] = type; |
487 if (currentType != type) { | 471 if (currentType != type) { |
488 inferrer.recordLocalUpdate(local, type); | 472 inferrer.recordLocalUpdate(local, type); |
489 } | 473 } |
490 } | 474 } |
491 if (capturedAndBoxed.containsKey(local)) { | 475 if (capturedAndBoxed.containsKey(local)) { |
492 inferrer.recordTypeOfNonFinalField( | 476 inferrer.recordTypeOfNonFinalField(node, capturedAndBoxed[local], type); |
493 node, capturedAndBoxed[local], type); | |
494 } else if (inTryBlock) { | 477 } else if (inTryBlock) { |
495 // We don't know if an assignment in a try block | 478 // We don't know if an assignment in a try block |
496 // will be executed, so all assigments in that block are | 479 // will be executed, so all assigments in that block are |
497 // potential types after we have left it. We update the parent | 480 // potential types after we have left it. We update the parent |
498 // of the try block so that, at exit of the try block, we get | 481 // of the try block so that, at exit of the try block, we get |
499 // the right phi for it. | 482 // the right phi for it. |
500 T existing = tryBlock.locals.parent[local]; | 483 T existing = tryBlock.locals.parent[local]; |
501 if (existing != null) { | 484 if (existing != null) { |
502 T phiType = types.allocatePhi(tryBlock.locals.block, local, existing); | 485 T phiType = types.allocatePhi(tryBlock.locals.block, local, existing); |
503 T inputType = types.addPhiInput(local, phiType, type); | 486 T inputType = types.addPhiInput(local, phiType, type); |
504 tryBlock.locals.parent[local] = inputType; | 487 tryBlock.locals.parent[local] = inputType; |
505 } | 488 } |
506 // Update the current handler unconditionnally with the new | 489 // Update the current handler unconditionnally with the new |
507 // type. | 490 // type. |
508 updateLocal(); | 491 updateLocal(); |
509 } else { | 492 } else { |
510 updateLocal(); | 493 updateLocal(); |
511 } | 494 } |
512 } | 495 } |
513 | 496 |
514 void setCaptured(Local local, Element field) { | 497 void setCaptured(Local local, Element field) { |
515 captured[local] = field; | 498 captured[local] = field; |
516 } | 499 } |
517 | 500 |
518 void setCapturedAndBoxed(Local local, Element field) { | 501 void setCapturedAndBoxed(Local local, Element field) { |
519 capturedAndBoxed[local] = field; | 502 capturedAndBoxed[local] = field; |
520 } | 503 } |
521 | 504 |
522 void mergeDiamondFlow(LocalsHandler<T> thenBranch, | 505 void mergeDiamondFlow( |
523 LocalsHandler<T> elseBranch) { | 506 LocalsHandler<T> thenBranch, LocalsHandler<T> elseBranch) { |
524 if (fieldScope != null && elseBranch != null) { | 507 if (fieldScope != null && elseBranch != null) { |
525 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); | 508 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); |
526 } | 509 } |
527 seenReturnOrThrow = thenBranch.seenReturnOrThrow | 510 seenReturnOrThrow = thenBranch.seenReturnOrThrow && |
528 && elseBranch != null | 511 elseBranch != null && |
529 && elseBranch.seenReturnOrThrow; | 512 elseBranch.seenReturnOrThrow; |
530 seenBreakOrContinue = thenBranch.seenBreakOrContinue | 513 seenBreakOrContinue = thenBranch.seenBreakOrContinue && |
531 && elseBranch != null | 514 elseBranch != null && |
532 && elseBranch.seenBreakOrContinue; | 515 elseBranch.seenBreakOrContinue; |
533 if (aborts) return; | 516 if (aborts) return; |
534 | 517 |
535 void mergeOneBranch(LocalsHandler<T> other) { | 518 void mergeOneBranch(LocalsHandler<T> other) { |
536 other.locals.forEachOwnLocal((Local local, T type) { | 519 other.locals.forEachOwnLocal((Local local, T type) { |
537 T myType = locals[local]; | 520 T myType = locals[local]; |
538 if (myType == null) return; // Variable is only defined in [other]. | 521 if (myType == null) return; // Variable is only defined in [other]. |
539 if (type == myType) return; | 522 if (type == myType) return; |
540 locals[local] = types.allocateDiamondPhi(myType, type); | 523 locals[local] = types.allocateDiamondPhi(myType, type); |
541 }); | 524 }); |
542 } | 525 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 * [: L: { | 588 * [: L: { |
606 * if (...) break; | 589 * if (...) break; |
607 * ... | 590 * ... |
608 * } | 591 * } |
609 * :] | 592 * :] |
610 * | 593 * |
611 * where [:this:] is the [LocalsHandler] for the paths through the | 594 * where [:this:] is the [LocalsHandler] for the paths through the |
612 * labeled statement that do not break out. | 595 * labeled statement that do not break out. |
613 */ | 596 */ |
614 void mergeAfterBreaks(List<LocalsHandler<T>> handlers, | 597 void mergeAfterBreaks(List<LocalsHandler<T>> handlers, |
615 {bool keepOwnLocals: true}) { | 598 {bool keepOwnLocals: true}) { |
616 Node level = locals.block; | 599 Node level = locals.block; |
617 // Use a separate locals handler to perform the merge in, so that Phi | 600 // Use a separate locals handler to perform the merge in, so that Phi |
618 // creation does not invalidate previous type knowledge while we might | 601 // creation does not invalidate previous type knowledge while we might |
619 // still look it up. | 602 // still look it up. |
620 LocalsHandler merged = new LocalsHandler.from(this, level); | 603 LocalsHandler merged = new LocalsHandler.from(this, level); |
621 Set<Local> seenLocals = new Setlet<Local>(); | 604 Set<Local> seenLocals = new Setlet<Local>(); |
622 bool allBranchesAbort = true; | 605 bool allBranchesAbort = true; |
623 // Merge all other handlers. | 606 // Merge all other handlers. |
624 for (LocalsHandler handler in handlers) { | 607 for (LocalsHandler handler in handlers) { |
625 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; | 608 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; |
626 merged.mergeHandler(handler, seenLocals); | 609 merged.mergeHandler(handler, seenLocals); |
627 } | 610 } |
628 // If we want to keep own locals, we merge [seenLocals] from [this] into | 611 // If we want to keep own locals, we merge [seenLocals] from [this] into |
629 // [merged] to update the Phi nodes with original values. | 612 // [merged] to update the Phi nodes with original values. |
630 if (keepOwnLocals && !seenReturnOrThrow) { | 613 if (keepOwnLocals && !seenReturnOrThrow) { |
631 for (Local variable in seenLocals) { | 614 for (Local variable in seenLocals) { |
632 T originalType = locals[variable]; | 615 T originalType = locals[variable]; |
633 if (originalType != null) { | 616 if (originalType != null) { |
634 merged.locals[variable] = types.addPhiInput(variable, | 617 merged.locals[variable] = types.addPhiInput( |
635 merged.locals[variable], | 618 variable, merged.locals[variable], originalType); |
636 originalType); | |
637 } | 619 } |
638 } | 620 } |
639 } | 621 } |
640 // Clean up Phi nodes with single input and store back result into | 622 // Clean up Phi nodes with single input and store back result into |
641 // actual locals handler. | 623 // actual locals handler. |
642 merged.locals.forEachOwnLocal((Local variable, T type) { | 624 merged.locals.forEachOwnLocal((Local variable, T type) { |
643 locals[variable] = types.simplifyPhi(level, variable, type); | 625 locals[variable] = types.simplifyPhi(level, variable, type); |
644 }); | 626 }); |
645 seenReturnOrThrow = allBranchesAbort && | 627 seenReturnOrThrow = |
646 (!keepOwnLocals || seenReturnOrThrow); | 628 allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); |
647 } | 629 } |
648 | 630 |
649 /** | 631 /** |
650 * Merge [other] into this handler. Returns whether a local in this | 632 * Merge [other] into this handler. Returns whether a local in this |
651 * has changed. If [seen] is not null, we allocate new Phi nodes | 633 * has changed. If [seen] is not null, we allocate new Phi nodes |
652 * unless the local is already present in the set [seen]. This effectively | 634 * unless the local is already present in the set [seen]. This effectively |
653 * overwrites the current type knowledge in this handler. | 635 * overwrites the current type knowledge in this handler. |
654 */ | 636 */ |
655 bool mergeHandler(LocalsHandler<T> other, [Set<Local> seen]) { | 637 bool mergeHandler(LocalsHandler<T> other, [Set<Local> seen]) { |
656 if (other.seenReturnOrThrow) return false; | 638 if (other.seenReturnOrThrow) return false; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
704 }); | 686 }); |
705 } | 687 } |
706 | 688 |
707 void updateField(Element element, T type) { | 689 void updateField(Element element, T type) { |
708 fieldScope.updateField(element, type); | 690 fieldScope.updateField(element, type); |
709 } | 691 } |
710 } | 692 } |
711 | 693 |
712 abstract class InferrerVisitor<T, E extends MinimalInferrerEngine<T>> | 694 abstract class InferrerVisitor<T, E extends MinimalInferrerEngine<T>> |
713 extends Visitor<T> | 695 extends Visitor<T> |
714 with SemanticSendResolvedMixin<T, dynamic>, | 696 with |
715 CompoundBulkMixin<T, dynamic>, | 697 SemanticSendResolvedMixin<T, dynamic>, |
716 SetIfNullBulkMixin<T, dynamic>, | 698 CompoundBulkMixin<T, dynamic>, |
717 PrefixBulkMixin<T, dynamic>, | 699 SetIfNullBulkMixin<T, dynamic>, |
718 PostfixBulkMixin<T, dynamic>, | 700 PrefixBulkMixin<T, dynamic>, |
719 ErrorBulkMixin<T, dynamic>, | 701 PostfixBulkMixin<T, dynamic>, |
720 NewBulkMixin<T, dynamic>, | 702 ErrorBulkMixin<T, dynamic>, |
721 SetBulkMixin<T, dynamic> | 703 NewBulkMixin<T, dynamic>, |
| 704 SetBulkMixin<T, dynamic> |
722 implements SemanticSendVisitor<T, dynamic> { | 705 implements SemanticSendVisitor<T, dynamic> { |
723 final Compiler compiler; | 706 final Compiler compiler; |
724 final AstElement analyzedElement; | 707 final AstElement analyzedElement; |
725 final TypeSystem<T> types; | 708 final TypeSystem<T> types; |
726 final E inferrer; | 709 final E inferrer; |
727 final Map<JumpTarget, List<LocalsHandler<T>>> breaksFor = | 710 final Map<JumpTarget, List<LocalsHandler<T>>> breaksFor = |
728 new Map<JumpTarget, List<LocalsHandler<T>>>(); | 711 new Map<JumpTarget, List<LocalsHandler<T>>>(); |
729 final Map<JumpTarget, List<LocalsHandler>> continuesFor = | 712 final Map<JumpTarget, List<LocalsHandler>> continuesFor = |
730 new Map<JumpTarget, List<LocalsHandler<T>>>(); | 713 new Map<JumpTarget, List<LocalsHandler<T>>>(); |
731 LocalsHandler<T> locals; | 714 LocalsHandler<T> locals; |
732 final List<T> cascadeReceiverStack = new List<T>(); | 715 final List<T> cascadeReceiverStack = new List<T>(); |
733 final TreeElements elements; | 716 final TreeElements elements; |
734 | 717 |
735 bool accumulateIsChecks = false; | 718 bool accumulateIsChecks = false; |
736 bool conditionIsSimple = false; | 719 bool conditionIsSimple = false; |
737 List<Send> isChecks; | 720 List<Send> isChecks; |
738 int loopLevel = 0; | 721 int loopLevel = 0; |
739 | 722 |
740 bool get inLoop => loopLevel > 0; | 723 bool get inLoop => loopLevel > 0; |
741 bool get isThisExposed { | 724 bool get isThisExposed { |
742 return analyzedElement.isGenerativeConstructor | 725 return analyzedElement.isGenerativeConstructor |
743 ? locals.fieldScope.isThisExposed | 726 ? locals.fieldScope.isThisExposed |
744 : true; | 727 : true; |
745 } | 728 } |
| 729 |
746 void set isThisExposed(value) { | 730 void set isThisExposed(value) { |
747 if (analyzedElement.isGenerativeConstructor) { | 731 if (analyzedElement.isGenerativeConstructor) { |
748 locals.fieldScope.isThisExposed = value; | 732 locals.fieldScope.isThisExposed = value; |
749 } | 733 } |
750 } | 734 } |
751 | 735 |
752 InferrerVisitor(AstElement analyzedElement, | 736 InferrerVisitor( |
753 this.inferrer, | 737 AstElement analyzedElement, this.inferrer, this.types, this.compiler, |
754 this.types, | 738 [LocalsHandler<T> handler]) |
755 this.compiler, | 739 : this.analyzedElement = analyzedElement, |
756 [LocalsHandler<T> handler]) | 740 this.locals = handler, |
757 : this.analyzedElement = analyzedElement, | 741 this.elements = analyzedElement.resolvedAst.elements { |
758 this.locals = handler, | |
759 this.elements = analyzedElement.resolvedAst.elements { | |
760 if (handler != null) return; | 742 if (handler != null) return; |
761 Node node = analyzedElement.node; | 743 Node node = analyzedElement.node; |
762 FieldInitializationScope<T> fieldScope = | 744 FieldInitializationScope<T> fieldScope = |
763 analyzedElement.isGenerativeConstructor | 745 analyzedElement.isGenerativeConstructor |
764 ? new FieldInitializationScope<T>(types) | 746 ? new FieldInitializationScope<T>(types) |
765 : null; | 747 : null; |
766 locals = new LocalsHandler<T>(inferrer, types, compiler, node, fieldScope); | 748 locals = new LocalsHandler<T>(inferrer, types, compiler, node, fieldScope); |
767 } | 749 } |
768 | 750 |
769 DiagnosticReporter get reporter => compiler.reporter; | 751 DiagnosticReporter get reporter => compiler.reporter; |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
898 } | 880 } |
899 | 881 |
900 T handleTypeLiteralGet() { | 882 T handleTypeLiteralGet() { |
901 return types.typeType; | 883 return types.typeType; |
902 } | 884 } |
903 | 885 |
904 T handleTypeLiteralInvoke(NodeList arguments) { | 886 T handleTypeLiteralInvoke(NodeList arguments) { |
905 return types.dynamicType; | 887 return types.dynamicType; |
906 } | 888 } |
907 | 889 |
908 | |
909 @override | 890 @override |
910 T bulkHandleNode(Node node, String message, _) { | 891 T bulkHandleNode(Node node, String message, _) { |
911 return internalError(node, message.replaceAll('#', '$node')); | 892 return internalError(node, message.replaceAll('#', '$node')); |
912 } | 893 } |
913 | 894 |
914 @override | 895 @override |
915 T visitConstantGet( | 896 T visitConstantGet(Send node, ConstantExpression constant, _) { |
916 Send node, | |
917 ConstantExpression constant, | |
918 _) { | |
919 return bulkHandleNode(node, "Constant read `#` unhandled.", _); | 897 return bulkHandleNode(node, "Constant read `#` unhandled.", _); |
920 } | 898 } |
921 | 899 |
922 @override | 900 @override |
923 T visitConstantInvoke( | 901 T visitConstantInvoke(Send node, ConstantExpression constant, |
924 Send node, | 902 NodeList arguments, CallStructure callStructure, _) { |
925 ConstantExpression constant, | |
926 NodeList arguments, | |
927 CallStructure callStructure, | |
928 _) { | |
929 return bulkHandleNode(node, "Constant invoke `#` unhandled.", _); | 903 return bulkHandleNode(node, "Constant invoke `#` unhandled.", _); |
930 } | 904 } |
931 | 905 |
932 T visitClassTypeLiteralGet( | 906 T visitClassTypeLiteralGet(Send node, ConstantExpression constant, _) { |
933 Send node, | |
934 ConstantExpression constant, | |
935 _) { | |
936 return handleTypeLiteralGet(); | 907 return handleTypeLiteralGet(); |
937 } | 908 } |
938 | 909 |
939 T visitClassTypeLiteralInvoke( | 910 T visitClassTypeLiteralInvoke(Send node, ConstantExpression constant, |
940 Send node, | 911 NodeList arguments, CallStructure callStructure, _) { |
941 ConstantExpression constant, | |
942 NodeList arguments, | |
943 CallStructure callStructure, | |
944 _) { | |
945 return handleTypeLiteralInvoke(arguments); | 912 return handleTypeLiteralInvoke(arguments); |
946 } | 913 } |
947 | 914 |
948 T visitTypedefTypeLiteralGet( | 915 T visitTypedefTypeLiteralGet(Send node, ConstantExpression constant, _) { |
949 Send node, | |
950 ConstantExpression constant, | |
951 _) { | |
952 return handleTypeLiteralGet(); | 916 return handleTypeLiteralGet(); |
953 } | 917 } |
954 | 918 |
955 T visitTypedefTypeLiteralInvoke( | 919 T visitTypedefTypeLiteralInvoke(Send node, ConstantExpression constant, |
956 Send node, | 920 NodeList arguments, CallStructure callStructure, _) { |
957 ConstantExpression constant, | |
958 NodeList arguments, | |
959 CallStructure callStructure, | |
960 _) { | |
961 return handleTypeLiteralInvoke(arguments); | 921 return handleTypeLiteralInvoke(arguments); |
962 } | 922 } |
963 | 923 |
964 T visitTypeVariableTypeLiteralGet( | 924 T visitTypeVariableTypeLiteralGet(Send node, TypeVariableElement element, _) { |
965 Send node, | |
966 TypeVariableElement element, | |
967 _) { | |
968 return handleTypeLiteralGet(); | 925 return handleTypeLiteralGet(); |
969 } | 926 } |
970 | 927 |
971 T visitTypeVariableTypeLiteralInvoke( | 928 T visitTypeVariableTypeLiteralInvoke(Send node, TypeVariableElement element, |
972 Send node, | 929 NodeList arguments, CallStructure callStructure, _) { |
973 TypeVariableElement element, | |
974 NodeList arguments, | |
975 CallStructure callStructure, | |
976 _) { | |
977 return handleTypeLiteralInvoke(arguments); | 930 return handleTypeLiteralInvoke(arguments); |
978 } | 931 } |
979 | 932 |
980 T visitDynamicTypeLiteralGet( | 933 T visitDynamicTypeLiteralGet(Send node, ConstantExpression constant, _) { |
981 Send node, | |
982 ConstantExpression constant, | |
983 _) { | |
984 return handleTypeLiteralGet(); | 934 return handleTypeLiteralGet(); |
985 } | 935 } |
986 | 936 |
987 T visitDynamicTypeLiteralInvoke( | 937 T visitDynamicTypeLiteralInvoke(Send node, ConstantExpression constant, |
988 Send node, | 938 NodeList arguments, CallStructure callStructure, _) { |
989 ConstantExpression constant, | |
990 NodeList arguments, | |
991 CallStructure callStructure, | |
992 _) { | |
993 return handleTypeLiteralInvoke(arguments); | 939 return handleTypeLiteralInvoke(arguments); |
994 } | 940 } |
995 | 941 |
996 bool isThisOrSuper(Node node) => node.isThis() || node.isSuper(); | 942 bool isThisOrSuper(Node node) => node.isThis() || node.isSuper(); |
997 | 943 |
998 Element get outermostElement { | 944 Element get outermostElement { |
999 return analyzedElement.outermostEnclosingMemberOrTopLevel.implementation; | 945 return analyzedElement.outermostEnclosingMemberOrTopLevel.implementation; |
1000 } | 946 } |
1001 | 947 |
1002 T _thisType; | 948 T _thisType; |
1003 T get thisType { | 949 T get thisType { |
1004 if (_thisType != null) return _thisType; | 950 if (_thisType != null) return _thisType; |
1005 ClassElement cls = outermostElement.enclosingClass; | 951 ClassElement cls = outermostElement.enclosingClass; |
1006 ClassWorld classWorld = compiler.world; | 952 ClassWorld classWorld = compiler.world; |
1007 if (classWorld.isUsedAsMixin(cls)) { | 953 if (classWorld.isUsedAsMixin(cls)) { |
1008 return _thisType = types.nonNullSubtype(cls); | 954 return _thisType = types.nonNullSubtype(cls); |
1009 } else { | 955 } else { |
1010 return _thisType = types.nonNullSubclass(cls); | 956 return _thisType = types.nonNullSubclass(cls); |
1011 } | 957 } |
1012 } | 958 } |
1013 | 959 |
1014 T _superType; | 960 T _superType; |
1015 T get superType { | 961 T get superType { |
1016 if (_superType != null) return _superType; | 962 if (_superType != null) return _superType; |
1017 return _superType = types.nonNullExact( | 963 return _superType = |
1018 outermostElement.enclosingClass.superclass); | 964 types.nonNullExact(outermostElement.enclosingClass.superclass); |
1019 } | 965 } |
1020 | 966 |
1021 @override | 967 @override |
1022 T visitThisGet(Identifier node, _) { | 968 T visitThisGet(Identifier node, _) { |
1023 return thisType; | 969 return thisType; |
1024 } | 970 } |
1025 | 971 |
1026 T visitIdentifier(Identifier node) { | 972 T visitIdentifier(Identifier node) { |
1027 if (node.isThis()) { | 973 if (node.isThis()) { |
1028 return thisType; | 974 return thisType; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1066 if (usePositive) continue; | 1012 if (usePositive) continue; |
1067 } else { | 1013 } else { |
1068 if (!usePositive) continue; | 1014 if (!usePositive) continue; |
1069 } | 1015 } |
1070 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | 1016 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); |
1071 narrow(elements[node.receiver], type, node); | 1017 narrow(elements[node.receiver], type, node); |
1072 } else { | 1018 } else { |
1073 Element receiverElement = elements[node.receiver]; | 1019 Element receiverElement = elements[node.receiver]; |
1074 Element argumentElement = elements[node.arguments.first]; | 1020 Element argumentElement = elements[node.arguments.first]; |
1075 String operator = node.selector.asOperator().source; | 1021 String operator = node.selector.asOperator().source; |
1076 if ((operator == '==' && usePositive) | 1022 if ((operator == '==' && usePositive) || |
1077 || (operator == '!=' && !usePositive)) { | 1023 (operator == '!=' && !usePositive)) { |
1078 // Type the elements as null. | 1024 // Type the elements as null. |
1079 if (Elements.isLocal(receiverElement)) { | 1025 if (Elements.isLocal(receiverElement)) { |
1080 locals.update(receiverElement, types.nullType, node); | 1026 locals.update(receiverElement, types.nullType, node); |
1081 } | 1027 } |
1082 if (Elements.isLocal(argumentElement)) { | 1028 if (Elements.isLocal(argumentElement)) { |
1083 locals.update(argumentElement, types.nullType, node); | 1029 locals.update(argumentElement, types.nullType, node); |
1084 } | 1030 } |
1085 } else { | 1031 } else { |
1086 // Narrow the elements to a non-null type. | 1032 // Narrow the elements to a non-null type. |
1087 DartType objectType = compiler.coreTypes.objectType; | 1033 DartType objectType = compiler.coreTypes.objectType; |
1088 if (Elements.isLocal(receiverElement)) { | 1034 if (Elements.isLocal(receiverElement)) { |
1089 narrow(receiverElement, objectType, node); | 1035 narrow(receiverElement, objectType, node); |
1090 } | 1036 } |
1091 if (Elements.isLocal(argumentElement)) { | 1037 if (Elements.isLocal(argumentElement)) { |
1092 narrow(argumentElement, objectType, node); | 1038 narrow(argumentElement, objectType, node); |
1093 } | 1039 } |
1094 } | 1040 } |
1095 } | 1041 } |
1096 } | 1042 } |
1097 } | 1043 } |
1098 | 1044 |
1099 @override | 1045 @override |
1100 T visitIndex(Send node, Node receiver, Node index, _) { | 1046 T visitIndex(Send node, Node receiver, Node index, _) { |
1101 return handleDynamicInvoke(node); | 1047 return handleDynamicInvoke(node); |
1102 } | 1048 } |
1103 | 1049 |
1104 @override | 1050 @override |
1105 T visitDynamicPropertyInvoke( | 1051 T visitDynamicPropertyInvoke( |
1106 Send node, | 1052 Send node, Node receiver, NodeList arguments, Selector selector, _) { |
1107 Node receiver, | |
1108 NodeList arguments, | |
1109 Selector selector, | |
1110 _) { | |
1111 return handleDynamicInvoke(node); | 1053 return handleDynamicInvoke(node); |
1112 } | 1054 } |
1113 | 1055 |
1114 @override | 1056 @override |
1115 T visitIfNotNullDynamicPropertyInvoke( | 1057 T visitIfNotNullDynamicPropertyInvoke( |
1116 Send node, | 1058 Send node, Node receiver, NodeList arguments, Selector selector, _) { |
1117 Node receiver, | |
1118 NodeList arguments, | |
1119 Selector selector, | |
1120 _) { | |
1121 return handleDynamicInvoke(node); | 1059 return handleDynamicInvoke(node); |
1122 } | 1060 } |
1123 | 1061 |
1124 @override | 1062 @override |
1125 T visitThisPropertyInvoke( | 1063 T visitThisPropertyInvoke( |
1126 Send node, | 1064 Send node, NodeList arguments, Selector selector, _) { |
1127 NodeList arguments, | |
1128 Selector selector, | |
1129 _) { | |
1130 return handleDynamicInvoke(node); | 1065 return handleDynamicInvoke(node); |
1131 } | 1066 } |
1132 | 1067 |
1133 @override | 1068 @override |
1134 T visitIfNull(Send node, Node left, Node right, _) { | 1069 T visitIfNull(Send node, Node left, Node right, _) { |
1135 T firstType = visit(left); | 1070 T firstType = visit(left); |
1136 T secondType = visit(right); | 1071 T secondType = visit(right); |
1137 return types.allocateDiamondPhi(types.narrowNotNull(firstType), secondType); | 1072 return types.allocateDiamondPhi(types.narrowNotNull(firstType), secondType); |
1138 } | 1073 } |
1139 | 1074 |
(...skipping 12 matching lines...) Expand all Loading... |
1152 updateIsChecks(isChecks, usePositive: true); | 1087 updateIsChecks(isChecks, usePositive: true); |
1153 LocalsHandler<T> narrowed; | 1088 LocalsHandler<T> narrowed; |
1154 if (oldAccumulateIsChecks) { | 1089 if (oldAccumulateIsChecks) { |
1155 narrowed = new LocalsHandler<T>.topLevelCopyOf(locals); | 1090 narrowed = new LocalsHandler<T>.topLevelCopyOf(locals); |
1156 } else { | 1091 } else { |
1157 accumulateIsChecks = false; | 1092 accumulateIsChecks = false; |
1158 isChecks = oldIsChecks; | 1093 isChecks = oldIsChecks; |
1159 } | 1094 } |
1160 visit(right); | 1095 visit(right); |
1161 if (oldAccumulateIsChecks) { | 1096 if (oldAccumulateIsChecks) { |
1162 | 1097 bool invalidatedInRightHandSide(Send test) { |
1163 bool invalidatedInRightHandSide (Send test) { | |
1164 Element receiver = elements[test.receiver]; | 1098 Element receiver = elements[test.receiver]; |
1165 if (receiver is LocalElement) { | 1099 if (receiver is LocalElement) { |
1166 return narrowed.locals[receiver] != locals.locals[receiver]; | 1100 return narrowed.locals[receiver] != locals.locals[receiver]; |
1167 } | 1101 } |
1168 return false; | 1102 return false; |
1169 } | 1103 } |
1170 | 1104 |
1171 isChecks.removeWhere(invalidatedInRightHandSide); | 1105 isChecks.removeWhere(invalidatedInRightHandSide); |
1172 } | 1106 } |
1173 saved.mergeDiamondFlow(locals, null); | 1107 saved.mergeDiamondFlow(locals, null); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1260 if (simpleCondition) updateIsChecks(tests, usePositive: false); | 1194 if (simpleCondition) updateIsChecks(tests, usePositive: false); |
1261 T secondType = visit(node.elseExpression); | 1195 T secondType = visit(node.elseExpression); |
1262 saved.mergeDiamondFlow(thenLocals, locals); | 1196 saved.mergeDiamondFlow(thenLocals, locals); |
1263 locals = saved; | 1197 locals = saved; |
1264 T type = types.allocateDiamondPhi(firstType, secondType); | 1198 T type = types.allocateDiamondPhi(firstType, secondType); |
1265 return type; | 1199 return type; |
1266 } | 1200 } |
1267 | 1201 |
1268 T visitVariableDefinitions(VariableDefinitions node) { | 1202 T visitVariableDefinitions(VariableDefinitions node) { |
1269 for (Link<Node> link = node.definitions.nodes; | 1203 for (Link<Node> link = node.definitions.nodes; |
1270 !link.isEmpty; | 1204 !link.isEmpty; |
1271 link = link.tail) { | 1205 link = link.tail) { |
1272 Node definition = link.head; | 1206 Node definition = link.head; |
1273 if (definition is Identifier) { | 1207 if (definition is Identifier) { |
1274 locals.update(elements[definition], types.nullType, node); | 1208 locals.update(elements[definition], types.nullType, node); |
1275 } else { | 1209 } else { |
1276 assert(definition.asSendSet() != null); | 1210 assert(definition.asSendSet() != null); |
1277 handleSendSet(definition); | 1211 handleSendSet(definition); |
1278 } | 1212 } |
1279 } | 1213 } |
1280 return null; | 1214 return null; |
1281 } | 1215 } |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1346 // Setup (and clear in case of multiple iterations of the loop) | 1280 // Setup (and clear in case of multiple iterations of the loop) |
1347 // the lists of breaks and continues seen in the loop. | 1281 // the lists of breaks and continues seen in the loop. |
1348 setupBreaksAndContinues(target); | 1282 setupBreaksAndContinues(target); |
1349 locals = new LocalsHandler<T>.from(saved, node); | 1283 locals = new LocalsHandler<T>.from(saved, node); |
1350 logic(); | 1284 logic(); |
1351 changed = saved.mergeAll(getLoopBackEdges(target)); | 1285 changed = saved.mergeAll(getLoopBackEdges(target)); |
1352 } while (changed); | 1286 } while (changed); |
1353 loopLevel--; | 1287 loopLevel--; |
1354 saved.endLoop(node); | 1288 saved.endLoop(node); |
1355 bool keepOwnLocals = node.asDoWhile() == null; | 1289 bool keepOwnLocals = node.asDoWhile() == null; |
1356 saved.mergeAfterBreaks( | 1290 saved.mergeAfterBreaks(getBreaks(target), keepOwnLocals: keepOwnLocals); |
1357 getBreaks(target), keepOwnLocals: keepOwnLocals); | |
1358 locals = saved; | 1291 locals = saved; |
1359 clearBreaksAndContinues(target); | 1292 clearBreaksAndContinues(target); |
1360 return null; | 1293 return null; |
1361 } | 1294 } |
1362 | 1295 |
1363 T visitWhile(While node) { | 1296 T visitWhile(While node) { |
1364 return handleLoop(node, () { | 1297 return handleLoop(node, () { |
1365 List<Send> tests = <Send>[]; | 1298 List<Send> tests = <Send>[]; |
1366 handleCondition(node.condition, tests); | 1299 handleCondition(node.condition, tests); |
1367 updateIsChecks(tests, usePositive: true); | 1300 updateIsChecks(tests, usePositive: true); |
(...skipping 16 matching lines...) Expand all Loading... |
1384 List<Send> tests = <Send>[]; | 1317 List<Send> tests = <Send>[]; |
1385 handleCondition(node.condition, tests); | 1318 handleCondition(node.condition, tests); |
1386 updateIsChecks(tests, usePositive: true); | 1319 updateIsChecks(tests, usePositive: true); |
1387 visit(node.body); | 1320 visit(node.body); |
1388 visit(node.update); | 1321 visit(node.update); |
1389 }); | 1322 }); |
1390 } | 1323 } |
1391 | 1324 |
1392 T visitTryStatement(TryStatement node) { | 1325 T visitTryStatement(TryStatement node) { |
1393 LocalsHandler<T> saved = locals; | 1326 LocalsHandler<T> saved = locals; |
1394 locals = new LocalsHandler<T>.from( | 1327 locals = new LocalsHandler<T>.from(locals, node, useOtherTryBlock: false); |
1395 locals, node, useOtherTryBlock: false); | |
1396 visit(node.tryBlock); | 1328 visit(node.tryBlock); |
1397 saved.mergeDiamondFlow(locals, null); | 1329 saved.mergeDiamondFlow(locals, null); |
1398 locals = saved; | 1330 locals = saved; |
1399 for (Node catchBlock in node.catchBlocks) { | 1331 for (Node catchBlock in node.catchBlocks) { |
1400 saved = locals; | 1332 saved = locals; |
1401 locals = new LocalsHandler<T>.from(locals, catchBlock); | 1333 locals = new LocalsHandler<T>.from(locals, catchBlock); |
1402 visit(catchBlock); | 1334 visit(catchBlock); |
1403 saved.mergeDiamondFlow(locals, null); | 1335 saved.mergeDiamondFlow(locals, null); |
1404 locals = saved; | 1336 locals = saved; |
1405 } | 1337 } |
1406 visit(node.finallyBlock); | 1338 visit(node.finallyBlock); |
1407 return null; | 1339 return null; |
1408 } | 1340 } |
1409 | 1341 |
1410 T visitThrow(Throw node) { | 1342 T visitThrow(Throw node) { |
1411 node.visitChildren(this); | 1343 node.visitChildren(this); |
1412 locals.seenReturnOrThrow = true; | 1344 locals.seenReturnOrThrow = true; |
1413 return types.nonNullEmpty(); | 1345 return types.nonNullEmpty(); |
1414 } | 1346 } |
1415 | 1347 |
1416 T visitCatchBlock(CatchBlock node) { | 1348 T visitCatchBlock(CatchBlock node) { |
1417 Node exception = node.exception; | 1349 Node exception = node.exception; |
1418 if (exception != null) { | 1350 if (exception != null) { |
1419 DartType type = elements.getType(node.type); | 1351 DartType type = elements.getType(node.type); |
1420 T mask = type == null || | 1352 T mask = type == null || type.treatAsDynamic || type.isTypeVariable |
1421 type.treatAsDynamic || | |
1422 type.isTypeVariable | |
1423 ? types.dynamicType | 1353 ? types.dynamicType |
1424 : types.nonNullSubtype(type.element); | 1354 : types.nonNullSubtype(type.element); |
1425 locals.update(elements[exception], mask, node); | 1355 locals.update(elements[exception], mask, node); |
1426 } | 1356 } |
1427 Node trace = node.trace; | 1357 Node trace = node.trace; |
1428 if (trace != null) { | 1358 if (trace != null) { |
1429 locals.update(elements[trace], types.dynamicType, node); | 1359 locals.update(elements[trace], types.dynamicType, node); |
1430 } | 1360 } |
1431 visit(node.block); | 1361 visit(node.block); |
1432 return null; | 1362 return null; |
1433 } | 1363 } |
1434 | 1364 |
1435 T visitParenthesizedExpression(ParenthesizedExpression node) { | 1365 T visitParenthesizedExpression(ParenthesizedExpression node) { |
1436 return visit(node.expression); | 1366 return visit(node.expression); |
1437 } | 1367 } |
1438 | 1368 |
1439 T visitBlock(Block node) { | 1369 T visitBlock(Block node) { |
1440 if (node.statements != null) { | 1370 if (node.statements != null) { |
1441 for (Node statement in node.statements) { | 1371 for (Node statement in node.statements) { |
1442 visit(statement); | 1372 visit(statement); |
1443 if (locals.aborts) break; | 1373 if (locals.aborts) break; |
1444 } | 1374 } |
1445 } | 1375 } |
1446 return null; | 1376 return null; |
1447 } | 1377 } |
1448 | 1378 |
1449 T visitLabeledStatement(LabeledStatement node) { | 1379 T visitLabeledStatement(LabeledStatement node) { |
1450 Statement body = node.statement; | 1380 Statement body = node.statement; |
1451 if (body is Loop | 1381 if (body is Loop || |
1452 || body is SwitchStatement | 1382 body is SwitchStatement || |
1453 || Elements.isUnusedLabel(node, elements)) { | 1383 Elements.isUnusedLabel(node, elements)) { |
1454 // Loops and switches handle their own labels. | 1384 // Loops and switches handle their own labels. |
1455 visit(body); | 1385 visit(body); |
1456 } else { | 1386 } else { |
1457 JumpTarget targetElement = elements.getTargetDefinition(body); | 1387 JumpTarget targetElement = elements.getTargetDefinition(body); |
1458 setupBreaksAndContinues(targetElement); | 1388 setupBreaksAndContinues(targetElement); |
1459 visit(body); | 1389 visit(body); |
1460 locals.mergeAfterBreaks(getBreaks(targetElement)); | 1390 locals.mergeAfterBreaks(getBreaks(targetElement)); |
1461 clearBreaksAndContinues(targetElement); | 1391 clearBreaksAndContinues(targetElement); |
1462 } | 1392 } |
1463 return null; | 1393 return null; |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1553 return type; | 1483 return type; |
1554 } | 1484 } |
1555 | 1485 |
1556 T visitCascade(Cascade node) { | 1486 T visitCascade(Cascade node) { |
1557 // Ignore the result of the cascade send and return the type of the cascade | 1487 // Ignore the result of the cascade send and return the type of the cascade |
1558 // receiver. | 1488 // receiver. |
1559 visit(node.expression); | 1489 visit(node.expression); |
1560 return cascadeReceiverStack.removeLast(); | 1490 return cascadeReceiverStack.removeLast(); |
1561 } | 1491 } |
1562 } | 1492 } |
OLD | NEW |