| 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 locals_handler; | 5 library locals_handler; |
| 6 | 6 |
| 7 import 'dart:collection' show IterableMixin; | 7 import 'dart:collection' show IterableMixin; |
| 8 | 8 |
| 9 import '../options.dart' show CompilerOptions; | 9 import '../options.dart' show CompilerOptions; |
| 10 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
| 11 import '../elements/entities.dart'; | 11 import '../elements/entities.dart'; |
| 12 import '../elements/types.dart'; |
| 12 import '../tree/tree.dart'; | 13 import '../tree/tree.dart'; |
| 13 import '../util/util.dart'; | 14 import '../util/util.dart'; |
| 14 import 'inferrer_engine.dart'; | 15 import 'inferrer_engine.dart'; |
| 15 import 'type_graph_nodes.dart'; | 16 import 'type_graph_nodes.dart'; |
| 16 import 'type_system.dart'; | 17 import 'type_system.dart'; |
| 17 | 18 |
| 18 /** | 19 /** |
| 19 * A variable scope holds types for variables. It has a link to a | 20 * A variable scope holds types for variables. It has a link to a |
| 20 * parent scope, but never changes the types in that parent. Instead, | 21 * parent scope, but never changes the types in that parent. Instead, |
| 21 * updates to locals of a parent scope are put in the current scope. | 22 * updates to locals of a parent scope are put in the current scope. |
| 22 * The inferrer makes sure updates get merged into the parent scope, | 23 * The inferrer makes sure updates get merged into the parent scope, |
| 23 * once the control flow block has been visited. | 24 * once the control flow block has been visited. |
| 24 */ | 25 */ |
| 25 class VariableScope { | 26 class VariableScope<T> { |
| 26 Map<Local, TypeInformation> variables; | 27 Map<Local, TypeInformation> variables; |
| 27 | 28 |
| 28 /// The parent of this scope. Null for the root scope. | 29 /// The parent of this scope. Null for the root scope. |
| 29 final VariableScope parent; | 30 final VariableScope parent; |
| 30 | 31 |
| 31 /// The [Node] that created this scope. | 32 /// The [Node] that created this scope. |
| 32 final Node block; | 33 final T block; |
| 33 | 34 |
| 34 VariableScope(this.block, [parent]) | 35 VariableScope(this.block, [parent]) |
| 35 : this.variables = null, | 36 : this.variables = null, |
| 36 this.parent = parent; | 37 this.parent = parent; |
| 37 | 38 |
| 38 VariableScope.deepCopyOf(VariableScope other) | 39 VariableScope.deepCopyOf(VariableScope<T> other) |
| 39 : variables = other.variables == null | 40 : variables = other.variables == null |
| 40 ? null | 41 ? null |
| 41 : new Map<Local, TypeInformation>.from(other.variables), | 42 : new Map<Local, TypeInformation>.from(other.variables), |
| 42 block = other.block, | 43 block = other.block, |
| 43 parent = other.parent == null | 44 parent = other.parent == null |
| 44 ? null | 45 ? null |
| 45 : new VariableScope.deepCopyOf(other.parent); | 46 : new VariableScope.deepCopyOf(other.parent); |
| 46 | 47 |
| 47 VariableScope.topLevelCopyOf(VariableScope other) | 48 VariableScope.topLevelCopyOf(VariableScope<T> other) |
| 48 : variables = other.variables == null | 49 : variables = other.variables == null |
| 49 ? null | 50 ? null |
| 50 : new Map<Local, TypeInformation>.from(other.variables), | 51 : new Map<Local, TypeInformation>.from(other.variables), |
| 51 block = other.block, | 52 block = other.block, |
| 52 parent = other.parent; | 53 parent = other.parent; |
| 53 | 54 |
| 54 TypeInformation operator [](Local variable) { | 55 TypeInformation operator [](Local variable) { |
| 55 TypeInformation result; | 56 TypeInformation result; |
| 56 if (variables == null || (result = variables[variable]) == null) { | 57 if (variables == null || (result = variables[variable]) == null) { |
| 57 return parent == null ? null : parent[variable]; | 58 return parent == null ? null : parent[variable]; |
| 58 } | 59 } |
| 59 return result; | 60 return result; |
| 60 } | 61 } |
| 61 | 62 |
| 62 void operator []=(Local variable, TypeInformation mask) { | 63 void operator []=(Local variable, TypeInformation mask) { |
| 63 assert(mask != null); | 64 assert(mask != null); |
| 64 if (variables == null) { | 65 if (variables == null) { |
| 65 variables = new Map<Local, TypeInformation>(); | 66 variables = new Map<Local, TypeInformation>(); |
| 66 } | 67 } |
| 67 variables[variable] = mask; | 68 variables[variable] = mask; |
| 68 } | 69 } |
| 69 | 70 |
| 70 void forEachOwnLocal(void f(Local variable, TypeInformation type)) { | 71 void forEachOwnLocal(void f(Local variable, TypeInformation type)) { |
| 71 if (variables == null) return; | 72 if (variables == null) return; |
| 72 variables.forEach(f); | 73 variables.forEach(f); |
| 73 } | 74 } |
| 74 | 75 |
| 75 void forEachLocalUntilNode( | 76 void forEachLocalUntilNode( |
| 76 Node node, void f(Local variable, TypeInformation type), | 77 T node, void f(Local variable, TypeInformation type), |
| 77 [Setlet<Local> seenLocals]) { | 78 [Setlet<Local> seenLocals]) { |
| 78 if (seenLocals == null) seenLocals = new Setlet<Local>(); | 79 if (seenLocals == null) seenLocals = new Setlet<Local>(); |
| 79 if (variables != null) { | 80 if (variables != null) { |
| 80 variables.forEach((variable, type) { | 81 variables.forEach((variable, type) { |
| 81 if (seenLocals.contains(variable)) return; | 82 if (seenLocals.contains(variable)) return; |
| 82 seenLocals.add(variable); | 83 seenLocals.add(variable); |
| 83 f(variable, type); | 84 f(variable, type); |
| 84 }); | 85 }); |
| 85 } | 86 } |
| 86 if (block == node) return; | 87 if (block == node) return; |
| 87 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); | 88 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); |
| 88 } | 89 } |
| 89 | 90 |
| 90 void forEachLocal(void f(Local variable, TypeInformation type)) { | 91 void forEachLocal(void f(Local variable, TypeInformation type)) { |
| 91 forEachLocalUntilNode(null, f); | 92 forEachLocalUntilNode(null, f); |
| 92 } | 93 } |
| 93 | 94 |
| 94 bool updates(Local variable) { | 95 bool updates(Local variable) { |
| 95 if (variables == null) return false; | 96 if (variables == null) return false; |
| 96 return variables.containsKey(variable); | 97 return variables.containsKey(variable); |
| 97 } | 98 } |
| 98 | 99 |
| 99 String toString() { | 100 String toString() { |
| 100 String rest = parent == null ? "null" : parent.toString(); | 101 String rest = parent == null ? "null" : parent.toString(); |
| 101 return '$variables $rest'; | 102 return '$variables $rest'; |
| 102 } | 103 } |
| 103 } | 104 } |
| 104 | 105 |
| 105 /// Tracks initializers via initializations and assignments. | 106 /// Tracks initializers via initializations and assignments. |
| 106 class FieldInitializationScope { | 107 class FieldInitializationScope<T> { |
| 107 final TypeSystem<Node> types; | 108 final TypeSystem<T> types; |
| 108 Map<Element, TypeInformation> fields; | 109 Map<FieldEntity, TypeInformation> fields; |
| 109 bool isThisExposed; | 110 bool isThisExposed; |
| 110 | 111 |
| 111 /// `true` when control flow prevents accumulating definite assignments, | 112 /// `true` when control flow prevents accumulating definite assignments, |
| 112 /// e.g. an early return or caught exception. | 113 /// e.g. an early return or caught exception. |
| 113 bool isIndefinite; | 114 bool isIndefinite; |
| 114 | 115 |
| 115 FieldInitializationScope(this.types) | 116 FieldInitializationScope(this.types) |
| 116 : isThisExposed = false, | 117 : isThisExposed = false, |
| 117 isIndefinite = false; | 118 isIndefinite = false; |
| 118 | 119 |
| 119 FieldInitializationScope.internalFrom(FieldInitializationScope other) | 120 FieldInitializationScope.internalFrom(FieldInitializationScope<T> other) |
| 120 : types = other.types, | 121 : types = other.types, |
| 121 isThisExposed = other.isThisExposed, | 122 isThisExposed = other.isThisExposed, |
| 122 isIndefinite = other.isIndefinite; | 123 isIndefinite = other.isIndefinite; |
| 123 | 124 |
| 124 factory FieldInitializationScope.from(FieldInitializationScope other) { | 125 factory FieldInitializationScope.from(FieldInitializationScope<T> other) { |
| 125 if (other == null) return null; | 126 if (other == null) return null; |
| 126 return new FieldInitializationScope.internalFrom(other); | 127 return new FieldInitializationScope<T>.internalFrom(other); |
| 127 } | 128 } |
| 128 | 129 |
| 129 void updateField(Element field, TypeInformation type) { | 130 void updateField(FieldEntity field, TypeInformation type) { |
| 130 if (isThisExposed) return; | 131 if (isThisExposed) return; |
| 131 if (isIndefinite) return; | 132 if (isIndefinite) return; |
| 132 fields ??= new Map<Element, TypeInformation>(); | 133 fields ??= new Map<FieldEntity, TypeInformation>(); |
| 133 fields[field] = type; | 134 fields[field] = type; |
| 134 } | 135 } |
| 135 | 136 |
| 136 TypeInformation readField(Element field) { | 137 TypeInformation readField(FieldEntity field) { |
| 137 return fields == null ? null : fields[field]; | 138 return fields == null ? null : fields[field]; |
| 138 } | 139 } |
| 139 | 140 |
| 140 void forEach(void f(Element element, TypeInformation type)) { | 141 void forEach(void f(FieldEntity element, TypeInformation type)) { |
| 141 fields?.forEach(f); | 142 fields?.forEach(f); |
| 142 } | 143 } |
| 143 | 144 |
| 144 void mergeDiamondFlow( | 145 void mergeDiamondFlow(FieldInitializationScope<T> thenScope, |
| 145 FieldInitializationScope thenScope, FieldInitializationScope elseScope) { | 146 FieldInitializationScope<T> elseScope) { |
| 146 // Quick bailout check. If [isThisExposed] or [isIndefinite] is true, we | 147 // Quick bailout check. If [isThisExposed] or [isIndefinite] is true, we |
| 147 // know the code following won'TypeInformation do anything. | 148 // know the code following won'TypeInformation do anything. |
| 148 if (isThisExposed) return; | 149 if (isThisExposed) return; |
| 149 if (isIndefinite) return; | 150 if (isIndefinite) return; |
| 150 | 151 |
| 151 FieldInitializationScope otherScope = | 152 FieldInitializationScope<T> otherScope = |
| 152 (elseScope == null || elseScope.fields == null) ? this : elseScope; | 153 (elseScope == null || elseScope.fields == null) ? this : elseScope; |
| 153 | 154 |
| 154 thenScope.forEach((Element field, TypeInformation type) { | 155 thenScope.forEach((FieldEntity field, TypeInformation type) { |
| 155 TypeInformation otherType = otherScope.readField(field); | 156 TypeInformation otherType = otherScope.readField(field); |
| 156 if (otherType == null) return; | 157 if (otherType == null) return; |
| 157 updateField(field, types.allocateDiamondPhi(type, otherType)); | 158 updateField(field, types.allocateDiamondPhi(type, otherType)); |
| 158 }); | 159 }); |
| 159 | 160 |
| 160 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; | 161 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; |
| 161 isIndefinite = thenScope.isIndefinite || elseScope.isIndefinite; | 162 isIndefinite = thenScope.isIndefinite || elseScope.isIndefinite; |
| 162 } | 163 } |
| 163 } | 164 } |
| 164 | 165 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 return true; | 235 return true; |
| 235 } | 236 } |
| 236 _iteratePositional = false; | 237 _iteratePositional = false; |
| 237 return named.moveNext(); | 238 return named.moveNext(); |
| 238 } | 239 } |
| 239 } | 240 } |
| 240 | 241 |
| 241 /** | 242 /** |
| 242 * Placeholder for inferred types of local variables. | 243 * Placeholder for inferred types of local variables. |
| 243 */ | 244 */ |
| 244 class LocalsHandler { | 245 class LocalsHandler<T> { |
| 245 final CompilerOptions options; | 246 final CompilerOptions options; |
| 246 final TypeSystem<Node> types; | 247 final TypeSystem<T> types; |
| 247 final InferrerEngine inferrer; | 248 final InferrerEngine<T> inferrer; |
| 248 final VariableScope locals; | 249 final VariableScope<T> locals; |
| 249 final Map<Local, FieldEntity> captured; | 250 final Map<Local, FieldEntity> captured; |
| 250 final Map<Local, FieldEntity> capturedAndBoxed; | 251 final Map<Local, FieldEntity> capturedAndBoxed; |
| 251 final FieldInitializationScope fieldScope; | 252 final FieldInitializationScope<T> fieldScope; |
| 252 LocalsHandler tryBlock; | 253 LocalsHandler<T> tryBlock; |
| 253 bool seenReturnOrThrow = false; | 254 bool seenReturnOrThrow = false; |
| 254 bool seenBreakOrContinue = false; | 255 bool seenBreakOrContinue = false; |
| 255 | 256 |
| 256 bool get aborts { | 257 bool get aborts { |
| 257 return seenReturnOrThrow || seenBreakOrContinue; | 258 return seenReturnOrThrow || seenBreakOrContinue; |
| 258 } | 259 } |
| 259 | 260 |
| 260 bool get inTryBlock => tryBlock != null; | 261 bool get inTryBlock => tryBlock != null; |
| 261 | 262 |
| 262 LocalsHandler(this.inferrer, this.types, this.options, Node block, | 263 LocalsHandler(this.inferrer, this.types, this.options, T block, |
| 263 [this.fieldScope]) | 264 [this.fieldScope]) |
| 264 : locals = new VariableScope(block), | 265 : locals = new VariableScope<T>(block), |
| 265 captured = new Map<Local, FieldEntity>(), | 266 captured = new Map<Local, FieldEntity>(), |
| 266 capturedAndBoxed = new Map<Local, FieldEntity>(), | 267 capturedAndBoxed = new Map<Local, FieldEntity>(), |
| 267 tryBlock = null; | 268 tryBlock = null; |
| 268 | 269 |
| 269 LocalsHandler.from(LocalsHandler other, Node block, | 270 LocalsHandler.from(LocalsHandler<T> other, T block, |
| 270 {bool useOtherTryBlock: true}) | 271 {bool useOtherTryBlock: true}) |
| 271 : locals = new VariableScope(block, other.locals), | 272 : locals = new VariableScope<T>(block, other.locals), |
| 272 fieldScope = new FieldInitializationScope.from(other.fieldScope), | 273 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), |
| 273 captured = other.captured, | 274 captured = other.captured, |
| 274 capturedAndBoxed = other.capturedAndBoxed, | 275 capturedAndBoxed = other.capturedAndBoxed, |
| 275 types = other.types, | 276 types = other.types, |
| 276 inferrer = other.inferrer, | 277 inferrer = other.inferrer, |
| 277 options = other.options { | 278 options = other.options { |
| 278 tryBlock = useOtherTryBlock ? other.tryBlock : this; | 279 tryBlock = useOtherTryBlock ? other.tryBlock : this; |
| 279 } | 280 } |
| 280 | 281 |
| 281 LocalsHandler.deepCopyOf(LocalsHandler other) | 282 LocalsHandler.deepCopyOf(LocalsHandler<T> other) |
| 282 : locals = new VariableScope.deepCopyOf(other.locals), | 283 : locals = new VariableScope<T>.deepCopyOf(other.locals), |
| 283 fieldScope = new FieldInitializationScope.from(other.fieldScope), | 284 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), |
| 284 captured = other.captured, | 285 captured = other.captured, |
| 285 capturedAndBoxed = other.capturedAndBoxed, | 286 capturedAndBoxed = other.capturedAndBoxed, |
| 286 tryBlock = other.tryBlock, | 287 tryBlock = other.tryBlock, |
| 287 types = other.types, | 288 types = other.types, |
| 288 inferrer = other.inferrer, | 289 inferrer = other.inferrer, |
| 289 options = other.options; | 290 options = other.options; |
| 290 | 291 |
| 291 LocalsHandler.topLevelCopyOf(LocalsHandler other) | 292 LocalsHandler.topLevelCopyOf(LocalsHandler<T> other) |
| 292 : locals = new VariableScope.topLevelCopyOf(other.locals), | 293 : locals = new VariableScope<T>.topLevelCopyOf(other.locals), |
| 293 fieldScope = new FieldInitializationScope.from(other.fieldScope), | 294 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), |
| 294 captured = other.captured, | 295 captured = other.captured, |
| 295 capturedAndBoxed = other.capturedAndBoxed, | 296 capturedAndBoxed = other.capturedAndBoxed, |
| 296 tryBlock = other.tryBlock, | 297 tryBlock = other.tryBlock, |
| 297 types = other.types, | 298 types = other.types, |
| 298 inferrer = other.inferrer, | 299 inferrer = other.inferrer, |
| 299 options = other.options; | 300 options = other.options; |
| 300 | 301 |
| 301 TypeInformation use(Local local) { | 302 TypeInformation use(Local local) { |
| 302 assert(!(local is LocalElement && !local.isImplementation)); | 303 assert(!(local is LocalElement && !local.isImplementation)); |
| 303 if (capturedAndBoxed.containsKey(local)) { | 304 if (capturedAndBoxed.containsKey(local)) { |
| 304 FieldElement field = capturedAndBoxed[local]; | 305 FieldElement field = capturedAndBoxed[local]; |
| 305 return inferrer.typeOfMember(field); | 306 return inferrer.typeOfMember(field); |
| 306 } else { | 307 } else { |
| 307 return locals[local]; | 308 return locals[local]; |
| 308 } | 309 } |
| 309 } | 310 } |
| 310 | 311 |
| 311 void update(LocalElement local, TypeInformation type, Node node) { | 312 void update(Local local, TypeInformation type, T node, DartType staticType, |
| 313 {bool isSetIfNull: false}) { |
| 312 assert(type != null); | 314 assert(type != null); |
| 313 if (options.trustTypeAnnotations || options.enableTypeAssertions) { | 315 if (options.trustTypeAnnotations || options.enableTypeAssertions) { |
| 314 type = types.narrowType(type, local.type); | 316 type = types.narrowType(type, staticType); |
| 315 } | 317 } |
| 316 updateLocal() { | 318 updateLocal() { |
| 317 TypeInformation currentType = locals[local]; | 319 TypeInformation currentType = locals[local]; |
| 318 | 320 |
| 319 SendSet send = node != null ? node.asSendSet() : null; | 321 if (isSetIfNull && currentType != null) { |
| 320 if (send != null && send.isIfNullAssignment && currentType != null) { | |
| 321 // If-null assignments may return either the new or the original value | 322 // If-null assignments may return either the new or the original value |
| 322 // narrowed to non-null. | 323 // narrowed to non-null. |
| 323 type = types.addPhiInput( | 324 type = types.addPhiInput( |
| 324 local, | 325 local, |
| 325 types.allocatePhi( | 326 types.allocatePhi( |
| 326 locals.block, local, types.narrowNotNull(currentType), | 327 locals.block, local, types.narrowNotNull(currentType), |
| 327 isTry: locals.block is TryStatement), | 328 isTry: locals.block is TryStatement), |
| 328 type); | 329 type); |
| 329 } | 330 } |
| 330 locals[local] = type; | 331 locals[local] = type; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 347 tryBlock.locals.parent[local] = inputType; | 348 tryBlock.locals.parent[local] = inputType; |
| 348 } | 349 } |
| 349 // Update the current handler unconditionnally with the new | 350 // Update the current handler unconditionnally with the new |
| 350 // type. | 351 // type. |
| 351 updateLocal(); | 352 updateLocal(); |
| 352 } else { | 353 } else { |
| 353 updateLocal(); | 354 updateLocal(); |
| 354 } | 355 } |
| 355 } | 356 } |
| 356 | 357 |
| 358 void narrow(Local local, DartType type, T node, {bool isSetIfNull: false}) { |
| 359 TypeInformation existing = use(local); |
| 360 TypeInformation newType = |
| 361 types.narrowType(existing, type, isNullable: false); |
| 362 update(local, newType, node, type, isSetIfNull: isSetIfNull); |
| 363 } |
| 364 |
| 357 void setCaptured(Local local, FieldEntity field) { | 365 void setCaptured(Local local, FieldEntity field) { |
| 358 captured[local] = field; | 366 captured[local] = field; |
| 359 } | 367 } |
| 360 | 368 |
| 361 void setCapturedAndBoxed(Local local, FieldEntity field) { | 369 void setCapturedAndBoxed(Local local, FieldEntity field) { |
| 362 capturedAndBoxed[local] = field; | 370 capturedAndBoxed[local] = field; |
| 363 } | 371 } |
| 364 | 372 |
| 365 void mergeDiamondFlow(LocalsHandler thenBranch, LocalsHandler elseBranch) { | 373 void mergeDiamondFlow( |
| 374 LocalsHandler<T> thenBranch, LocalsHandler<T> elseBranch) { |
| 366 if (fieldScope != null && elseBranch != null) { | 375 if (fieldScope != null && elseBranch != null) { |
| 367 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); | 376 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); |
| 368 } | 377 } |
| 369 seenReturnOrThrow = thenBranch.seenReturnOrThrow && | 378 seenReturnOrThrow = thenBranch.seenReturnOrThrow && |
| 370 elseBranch != null && | 379 elseBranch != null && |
| 371 elseBranch.seenReturnOrThrow; | 380 elseBranch.seenReturnOrThrow; |
| 372 seenBreakOrContinue = thenBranch.seenBreakOrContinue && | 381 seenBreakOrContinue = thenBranch.seenBreakOrContinue && |
| 373 elseBranch != null && | 382 elseBranch != null && |
| 374 elseBranch.seenBreakOrContinue; | 383 elseBranch.seenBreakOrContinue; |
| 375 if (aborts) return; | 384 if (aborts) return; |
| 376 | 385 |
| 377 void mergeOneBranch(LocalsHandler other) { | 386 void mergeOneBranch(LocalsHandler<T> other) { |
| 378 other.locals.forEachOwnLocal((Local local, TypeInformation type) { | 387 other.locals.forEachOwnLocal((Local local, TypeInformation type) { |
| 379 TypeInformation myType = locals[local]; | 388 TypeInformation myType = locals[local]; |
| 380 if (myType == null) return; // Variable is only defined in [other]. | 389 if (myType == null) return; // Variable is only defined in [other]. |
| 381 if (type == myType) return; | 390 if (type == myType) return; |
| 382 locals[local] = types.allocateDiamondPhi(myType, type); | 391 locals[local] = types.allocateDiamondPhi(myType, type); |
| 383 }); | 392 }); |
| 384 } | 393 } |
| 385 | 394 |
| 386 void inPlaceUpdateOneBranch(LocalsHandler other) { | 395 void inPlaceUpdateOneBranch(LocalsHandler<T> other) { |
| 387 other.locals.forEachOwnLocal((Local local, TypeInformation type) { | 396 other.locals.forEachOwnLocal((Local local, TypeInformation type) { |
| 388 TypeInformation myType = locals[local]; | 397 TypeInformation myType = locals[local]; |
| 389 if (myType == null) return; // Variable is only defined in [other]. | 398 if (myType == null) return; // Variable is only defined in [other]. |
| 390 if (type == myType) return; | 399 if (type == myType) return; |
| 391 locals[local] = type; | 400 locals[local] = type; |
| 392 }); | 401 }); |
| 393 } | 402 } |
| 394 | 403 |
| 395 if (thenBranch.aborts) { | 404 if (thenBranch.aborts) { |
| 396 if (elseBranch == null) return; | 405 if (elseBranch == null) return; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 * | 455 * |
| 447 * [: L: { | 456 * [: L: { |
| 448 * if (...) break; | 457 * if (...) break; |
| 449 * ... | 458 * ... |
| 450 * } | 459 * } |
| 451 * :] | 460 * :] |
| 452 * | 461 * |
| 453 * where [:this:] is the [LocalsHandler] for the paths through the | 462 * where [:this:] is the [LocalsHandler] for the paths through the |
| 454 * labeled statement that do not break out. | 463 * labeled statement that do not break out. |
| 455 */ | 464 */ |
| 456 void mergeAfterBreaks(List<LocalsHandler> handlers, | 465 void mergeAfterBreaks(List<LocalsHandler<T>> handlers, |
| 457 {bool keepOwnLocals: true}) { | 466 {bool keepOwnLocals: true}) { |
| 458 Node level = locals.block; | 467 T level = locals.block; |
| 459 // Use a separate locals handler to perform the merge in, so that Phi | 468 // Use a separate locals handler to perform the merge in, so that Phi |
| 460 // creation does not invalidate previous type knowledge while we might | 469 // creation does not invalidate previous type knowledge while we might |
| 461 // still look it up. | 470 // still look it up. |
| 462 LocalsHandler merged = new LocalsHandler.from(this, level); | 471 LocalsHandler<T> merged = new LocalsHandler<T>.from(this, level); |
| 463 Set<Local> seenLocals = new Setlet<Local>(); | 472 Set<Local> seenLocals = new Setlet<Local>(); |
| 464 bool allBranchesAbort = true; | 473 bool allBranchesAbort = true; |
| 465 // Merge all other handlers. | 474 // Merge all other handlers. |
| 466 for (LocalsHandler handler in handlers) { | 475 for (LocalsHandler<T> handler in handlers) { |
| 467 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; | 476 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; |
| 468 merged.mergeHandler(handler, seenLocals); | 477 merged.mergeHandler(handler, seenLocals); |
| 469 } | 478 } |
| 470 // If we want to keep own locals, we merge [seenLocals] from [this] into | 479 // If we want to keep own locals, we merge [seenLocals] from [this] into |
| 471 // [merged] to update the Phi nodes with original values. | 480 // [merged] to update the Phi nodes with original values. |
| 472 if (keepOwnLocals && !seenReturnOrThrow) { | 481 if (keepOwnLocals && !seenReturnOrThrow) { |
| 473 for (Local variable in seenLocals) { | 482 for (Local variable in seenLocals) { |
| 474 TypeInformation originalType = locals[variable]; | 483 TypeInformation originalType = locals[variable]; |
| 475 if (originalType != null) { | 484 if (originalType != null) { |
| 476 merged.locals[variable] = types.addPhiInput( | 485 merged.locals[variable] = types.addPhiInput( |
| 477 variable, merged.locals[variable], originalType); | 486 variable, merged.locals[variable], originalType); |
| 478 } | 487 } |
| 479 } | 488 } |
| 480 } | 489 } |
| 481 // Clean up Phi nodes with single input and store back result into | 490 // Clean up Phi nodes with single input and store back result into |
| 482 // actual locals handler. | 491 // actual locals handler. |
| 483 merged.locals.forEachOwnLocal((Local variable, TypeInformation type) { | 492 merged.locals.forEachOwnLocal((Local variable, TypeInformation type) { |
| 484 locals[variable] = types.simplifyPhi(level, variable, type); | 493 locals[variable] = types.simplifyPhi(level, variable, type); |
| 485 }); | 494 }); |
| 486 seenReturnOrThrow = | 495 seenReturnOrThrow = |
| 487 allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); | 496 allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); |
| 488 } | 497 } |
| 489 | 498 |
| 490 /** | 499 /** |
| 491 * Merge [other] into this handler. Returns whether a local in this | 500 * Merge [other] into this handler. Returns whether a local in this |
| 492 * has changed. If [seen] is not null, we allocate new Phi nodes | 501 * has changed. If [seen] is not null, we allocate new Phi nodes |
| 493 * unless the local is already present in the set [seen]. This effectively | 502 * unless the local is already present in the set [seen]. This effectively |
| 494 * overwrites the current type knowledge in this handler. | 503 * overwrites the current type knowledge in this handler. |
| 495 */ | 504 */ |
| 496 bool mergeHandler(LocalsHandler other, [Set<Local> seen]) { | 505 bool mergeHandler(LocalsHandler<T> other, [Set<Local> seen]) { |
| 497 if (other.seenReturnOrThrow) return false; | 506 if (other.seenReturnOrThrow) return false; |
| 498 bool changed = false; | 507 bool changed = false; |
| 499 other.locals.forEachLocalUntilNode(locals.block, (local, otherType) { | 508 other.locals.forEachLocalUntilNode(locals.block, (local, otherType) { |
| 500 TypeInformation myType = locals[local]; | 509 TypeInformation myType = locals[local]; |
| 501 if (myType == null) return; | 510 if (myType == null) return; |
| 502 TypeInformation newType; | 511 TypeInformation newType; |
| 503 if (seen != null && !seen.contains(local)) { | 512 if (seen != null && !seen.contains(local)) { |
| 504 newType = types.allocatePhi(locals.block, local, otherType, | 513 newType = types.allocatePhi(locals.block, local, otherType, |
| 505 isTry: locals.block is TryStatement); | 514 isTry: locals.block is TryStatement); |
| 506 seen.add(local); | 515 seen.add(local); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 521 */ | 530 */ |
| 522 bool mergeAll(List<LocalsHandler> handlers) { | 531 bool mergeAll(List<LocalsHandler> handlers) { |
| 523 bool changed = false; | 532 bool changed = false; |
| 524 assert(!seenReturnOrThrow); | 533 assert(!seenReturnOrThrow); |
| 525 handlers.forEach((other) { | 534 handlers.forEach((other) { |
| 526 changed = mergeHandler(other) || changed; | 535 changed = mergeHandler(other) || changed; |
| 527 }); | 536 }); |
| 528 return changed; | 537 return changed; |
| 529 } | 538 } |
| 530 | 539 |
| 531 void startLoop(Node loop) { | 540 void startLoop(T loop) { |
| 532 locals.forEachLocal((Local variable, TypeInformation type) { | 541 locals.forEachLocal((Local variable, TypeInformation type) { |
| 533 TypeInformation newType = types.allocateLoopPhi(loop, variable, type, | 542 TypeInformation newType = types.allocateLoopPhi(loop, variable, type, |
| 534 isTry: loop is TryStatement); | 543 isTry: loop is TryStatement); |
| 535 if (newType != type) { | 544 if (newType != type) { |
| 536 locals[variable] = newType; | 545 locals[variable] = newType; |
| 537 } | 546 } |
| 538 }); | 547 }); |
| 539 } | 548 } |
| 540 | 549 |
| 541 void endLoop(Node loop) { | 550 void endLoop(T loop) { |
| 542 locals.forEachLocal((Local variable, TypeInformation type) { | 551 locals.forEachLocal((Local variable, TypeInformation type) { |
| 543 TypeInformation newType = types.simplifyPhi(loop, variable, type); | 552 TypeInformation newType = types.simplifyPhi(loop, variable, type); |
| 544 if (newType != type) { | 553 if (newType != type) { |
| 545 locals[variable] = newType; | 554 locals[variable] = newType; |
| 546 } | 555 } |
| 547 }); | 556 }); |
| 548 } | 557 } |
| 549 | 558 |
| 550 void updateField(Element element, TypeInformation type) { | 559 void updateField(FieldEntity element, TypeInformation type) { |
| 551 fieldScope.updateField(element, type); | 560 fieldScope.updateField(element, type); |
| 552 } | 561 } |
| 553 } | 562 } |
| OLD | NEW |