| 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 IterableMixin; | 7 import 'dart:collection' show IterableMixin; |
| 8 | 8 |
| 9 import '../common.dart'; | 9 import '../common.dart'; |
| 10 import '../options.dart' show CompilerOptions; | 10 import '../options.dart' show CompilerOptions; |
| 11 import '../compiler.dart' show Compiler; | 11 import '../compiler.dart' show Compiler; |
| 12 import '../constants/constant_system.dart'; | 12 import '../constants/constant_system.dart'; |
| 13 import '../constants/expressions.dart'; | 13 import '../constants/expressions.dart'; |
| 14 import '../elements/resolution_types.dart'; | 14 import '../elements/resolution_types.dart'; |
| 15 import '../elements/elements.dart'; | 15 import '../elements/elements.dart'; |
| 16 import '../resolution/operators.dart'; | 16 import '../resolution/operators.dart'; |
| 17 import '../resolution/semantic_visitor.dart'; | 17 import '../resolution/semantic_visitor.dart'; |
| 18 import '../resolution/tree_elements.dart' show TreeElements; | 18 import '../resolution/tree_elements.dart' show TreeElements; |
| 19 import '../tree/tree.dart'; | 19 import '../tree/tree.dart'; |
| 20 import '../types/constants.dart' show computeTypeMask; | 20 import '../types/constants.dart' show computeTypeMask; |
| 21 import '../types/types.dart' show TypeMask; | 21 import '../types/types.dart' show TypeMask; |
| 22 import '../universe/call_structure.dart' show CallStructure; | 22 import '../universe/call_structure.dart' show CallStructure; |
| 23 import '../universe/selector.dart' show Selector; | 23 import '../universe/selector.dart' show Selector; |
| 24 import '../util/util.dart'; | 24 import '../util/util.dart'; |
| 25 import '../world.dart' show ClosedWorld; | 25 import '../world.dart' show ClosedWorld; |
| 26 | 26 import 'inferrer_engine.dart'; |
| 27 /** | 27 import 'type_graph_nodes.dart'; |
| 28 * The interface [InferrerVisitor] will use when working on types. | 28 import 'type_system.dart'; |
| 29 */ | |
| 30 abstract class TypeSystem<T> { | |
| 31 T get dynamicType; | |
| 32 T get nullType; | |
| 33 T get intType; | |
| 34 T get uint31Type; | |
| 35 T get uint32Type; | |
| 36 T get positiveIntType; | |
| 37 T get doubleType; | |
| 38 T get numType; | |
| 39 T get boolType; | |
| 40 T get functionType; | |
| 41 T get listType; | |
| 42 T get constListType; | |
| 43 T get fixedListType; | |
| 44 T get growableListType; | |
| 45 T get mapType; | |
| 46 T get constMapType; | |
| 47 T get stringType; | |
| 48 T get typeType; | |
| 49 T get syncStarIterableType; | |
| 50 T get asyncFutureType; // Subtype of Future returned by async methods. | |
| 51 T get asyncStarStreamType; | |
| 52 | |
| 53 T stringLiteralType(DartString value); | |
| 54 T boolLiteralType(LiteralBool value); | |
| 55 | |
| 56 T nonNullSubtype(ClassElement type); | |
| 57 T nonNullSubclass(ClassElement type); | |
| 58 T nonNullExact(ClassElement type); | |
| 59 T nonNullEmpty(); | |
| 60 bool isNull(T type); | |
| 61 TypeMask newTypedSelector(T receiver, TypeMask mask); | |
| 62 | |
| 63 T allocateList(T type, Node node, Element enclosing, | |
| 64 [T elementType, int length]); | |
| 65 | |
| 66 T allocateMap(T type, Node node, Element element, | |
| 67 [List<T> keyType, List<T> valueType]); | |
| 68 | |
| 69 T allocateClosure(Node node, Element element); | |
| 70 | |
| 71 /** | |
| 72 * Returns the least upper bound between [firstType] and | |
| 73 * [secondType]. | |
| 74 */ | |
| 75 T computeLUB(T firstType, T secondType); | |
| 76 | |
| 77 /** | |
| 78 * Returns the intersection between [T] and [annotation]. | |
| 79 * [isNullable] indicates whether the annotation implies a null | |
| 80 * type. | |
| 81 */ | |
| 82 T narrowType(T type, ResolutionDartType annotation, {bool isNullable: true}); | |
| 83 | |
| 84 /** | |
| 85 * Returns the non-nullable type [T]. | |
| 86 */ | |
| 87 T narrowNotNull(T type); | |
| 88 | |
| 89 /** | |
| 90 * Returns a new type that unions [firstInput] and [secondInput]. | |
| 91 */ | |
| 92 T allocateDiamondPhi(T firstInput, T secondInput); | |
| 93 | |
| 94 /** | |
| 95 * Returns a new type for holding the potential types of [element]. | |
| 96 * [inputType] is the first incoming type of the phi. | |
| 97 */ | |
| 98 T allocatePhi(Node node, Local variable, T inputType); | |
| 99 | |
| 100 /** | |
| 101 * Returns a new type for holding the potential types of [element]. | |
| 102 * [inputType] is the first incoming type of the phi. [allocateLoopPhi] | |
| 103 * only differs from [allocatePhi] in that it allows the underlying | |
| 104 * implementation of [TypeSystem] to differentiate Phi nodes due to loops | |
| 105 * from other merging uses. | |
| 106 */ | |
| 107 T allocateLoopPhi(Node node, Local variable, T inputType); | |
| 108 | |
| 109 /** | |
| 110 * Simplies the phi representing [element] and of the type | |
| 111 * [phiType]. For example, if this phi has one incoming input, an | |
| 112 * implementation of this method could just return that incoming | |
| 113 * input type. | |
| 114 */ | |
| 115 T simplifyPhi(Node node, Local variable, T phiType); | |
| 116 | |
| 117 /** | |
| 118 * Adds [newType] as an input of [phiType]. | |
| 119 */ | |
| 120 T addPhiInput(Local variable, T phiType, T newType); | |
| 121 | |
| 122 /** | |
| 123 * Returns `true` if `selector` should be updated to reflect the new | |
| 124 * `receiverType`. | |
| 125 */ | |
| 126 bool selectorNeedsUpdate(T receiverType, TypeMask mask); | |
| 127 | |
| 128 /** | |
| 129 * Returns a new receiver type for this [selector] applied to | |
| 130 * [receiverType]. | |
| 131 * | |
| 132 * The option [isConditional] is true when [selector] was seen in a | |
| 133 * conditional send (e.g. `a?.selector`), in which case the returned type may | |
| 134 * be null. | |
| 135 */ | |
| 136 T refineReceiver( | |
| 137 Selector selector, TypeMask mask, T receiverType, bool isConditional); | |
| 138 | |
| 139 /** | |
| 140 * Returns the internal inferrer representation for [mask]. | |
| 141 */ | |
| 142 T getConcreteTypeFor(TypeMask mask); | |
| 143 } | |
| 144 | 29 |
| 145 /** | 30 /** |
| 146 * A variable scope holds types for variables. It has a link to a | 31 * A variable scope holds types for variables. It has a link to a |
| 147 * parent scope, but never changes the types in that parent. Instead, | 32 * parent scope, but never changes the types in that parent. Instead, |
| 148 * updates to locals of a parent scope are put in the current scope. | 33 * updates to locals of a parent scope are put in the current scope. |
| 149 * The inferrer makes sure updates get merged into the parent scope, | 34 * The inferrer makes sure updates get merged into the parent scope, |
| 150 * once the control flow block has been visited. | 35 * once the control flow block has been visited. |
| 151 */ | 36 */ |
| 152 class VariableScope<T> { | 37 class VariableScope { |
| 153 Map<Local, T> variables; | 38 Map<Local, TypeInformation> variables; |
| 154 | 39 |
| 155 /// The parent of this scope. Null for the root scope. | 40 /// The parent of this scope. Null for the root scope. |
| 156 final VariableScope<T> parent; | 41 final VariableScope parent; |
| 157 | 42 |
| 158 /// The [Node] that created this scope. | 43 /// The [Node] that created this scope. |
| 159 final Node block; | 44 final Node block; |
| 160 | 45 |
| 161 VariableScope(this.block, [parent]) | 46 VariableScope(this.block, [parent]) |
| 162 : this.variables = null, | 47 : this.variables = null, |
| 163 this.parent = parent; | 48 this.parent = parent; |
| 164 | 49 |
| 165 VariableScope.deepCopyOf(VariableScope<T> other) | 50 VariableScope.deepCopyOf(VariableScope other) |
| 166 : variables = other.variables == null | 51 : variables = other.variables == null |
| 167 ? null | 52 ? null |
| 168 : new Map<Local, T>.from(other.variables), | 53 : new Map<Local, TypeInformation>.from(other.variables), |
| 169 block = other.block, | 54 block = other.block, |
| 170 parent = other.parent == null | 55 parent = other.parent == null |
| 171 ? null | 56 ? null |
| 172 : new VariableScope<T>.deepCopyOf(other.parent); | 57 : new VariableScope.deepCopyOf(other.parent); |
| 173 | 58 |
| 174 VariableScope.topLevelCopyOf(VariableScope<T> other) | 59 VariableScope.topLevelCopyOf(VariableScope other) |
| 175 : variables = other.variables == null | 60 : variables = other.variables == null |
| 176 ? null | 61 ? null |
| 177 : new Map<Local, T>.from(other.variables), | 62 : new Map<Local, TypeInformation>.from(other.variables), |
| 178 block = other.block, | 63 block = other.block, |
| 179 parent = other.parent; | 64 parent = other.parent; |
| 180 | 65 |
| 181 T operator [](Local variable) { | 66 TypeInformation operator [](Local variable) { |
| 182 T result; | 67 TypeInformation result; |
| 183 if (variables == null || (result = variables[variable]) == null) { | 68 if (variables == null || (result = variables[variable]) == null) { |
| 184 return parent == null ? null : parent[variable]; | 69 return parent == null ? null : parent[variable]; |
| 185 } | 70 } |
| 186 return result; | 71 return result; |
| 187 } | 72 } |
| 188 | 73 |
| 189 void operator []=(Local variable, T mask) { | 74 void operator []=(Local variable, TypeInformation mask) { |
| 190 assert(mask != null); | 75 assert(mask != null); |
| 191 if (variables == null) { | 76 if (variables == null) { |
| 192 variables = new Map<Local, T>(); | 77 variables = new Map<Local, TypeInformation>(); |
| 193 } | 78 } |
| 194 variables[variable] = mask; | 79 variables[variable] = mask; |
| 195 } | 80 } |
| 196 | 81 |
| 197 void forEachOwnLocal(void f(Local variable, T type)) { | 82 void forEachOwnLocal(void f(Local variable, TypeInformation type)) { |
| 198 if (variables == null) return; | 83 if (variables == null) return; |
| 199 variables.forEach(f); | 84 variables.forEach(f); |
| 200 } | 85 } |
| 201 | 86 |
| 202 void forEachLocalUntilNode(Node node, void f(Local variable, T type), | 87 void forEachLocalUntilNode( |
| 88 Node node, void f(Local variable, TypeInformation type), |
| 203 [Setlet<Local> seenLocals]) { | 89 [Setlet<Local> seenLocals]) { |
| 204 if (seenLocals == null) seenLocals = new Setlet<Local>(); | 90 if (seenLocals == null) seenLocals = new Setlet<Local>(); |
| 205 if (variables != null) { | 91 if (variables != null) { |
| 206 variables.forEach((variable, type) { | 92 variables.forEach((variable, type) { |
| 207 if (seenLocals.contains(variable)) return; | 93 if (seenLocals.contains(variable)) return; |
| 208 seenLocals.add(variable); | 94 seenLocals.add(variable); |
| 209 f(variable, type); | 95 f(variable, type); |
| 210 }); | 96 }); |
| 211 } | 97 } |
| 212 if (block == node) return; | 98 if (block == node) return; |
| 213 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); | 99 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals); |
| 214 } | 100 } |
| 215 | 101 |
| 216 void forEachLocal(void f(Local variable, T type)) { | 102 void forEachLocal(void f(Local variable, TypeInformation type)) { |
| 217 forEachLocalUntilNode(null, f); | 103 forEachLocalUntilNode(null, f); |
| 218 } | 104 } |
| 219 | 105 |
| 220 bool updates(Local variable) { | 106 bool updates(Local variable) { |
| 221 if (variables == null) return false; | 107 if (variables == null) return false; |
| 222 return variables.containsKey(variable); | 108 return variables.containsKey(variable); |
| 223 } | 109 } |
| 224 | 110 |
| 225 String toString() { | 111 String toString() { |
| 226 String rest = parent == null ? "null" : parent.toString(); | 112 String rest = parent == null ? "null" : parent.toString(); |
| 227 return '$variables $rest'; | 113 return '$variables $rest'; |
| 228 } | 114 } |
| 229 } | 115 } |
| 230 | 116 |
| 231 class FieldInitializationScope<T> { | 117 class FieldInitializationScope { |
| 232 final TypeSystem<T> types; | 118 final TypeSystem types; |
| 233 Map<Element, T> fields; | 119 Map<Element, TypeInformation> fields; |
| 234 bool isThisExposed; | 120 bool isThisExposed; |
| 235 | 121 |
| 236 FieldInitializationScope(this.types) : isThisExposed = false; | 122 FieldInitializationScope(this.types) : isThisExposed = false; |
| 237 | 123 |
| 238 FieldInitializationScope.internalFrom(FieldInitializationScope<T> other) | 124 FieldInitializationScope.internalFrom(FieldInitializationScope other) |
| 239 : types = other.types, | 125 : types = other.types, |
| 240 isThisExposed = other.isThisExposed; | 126 isThisExposed = other.isThisExposed; |
| 241 | 127 |
| 242 factory FieldInitializationScope.from(FieldInitializationScope<T> other) { | 128 factory FieldInitializationScope.from(FieldInitializationScope other) { |
| 243 if (other == null) return null; | 129 if (other == null) return null; |
| 244 return new FieldInitializationScope<T>.internalFrom(other); | 130 return new FieldInitializationScope.internalFrom(other); |
| 245 } | 131 } |
| 246 | 132 |
| 247 void updateField(Element field, T type) { | 133 void updateField(Element field, TypeInformation type) { |
| 248 if (isThisExposed) return; | 134 if (isThisExposed) return; |
| 249 if (fields == null) fields = new Map<Element, T>(); | 135 if (fields == null) fields = new Map<Element, TypeInformation>(); |
| 250 fields[field] = type; | 136 fields[field] = type; |
| 251 } | 137 } |
| 252 | 138 |
| 253 T readField(Element field) { | 139 TypeInformation readField(Element field) { |
| 254 return fields == null ? null : fields[field]; | 140 return fields == null ? null : fields[field]; |
| 255 } | 141 } |
| 256 | 142 |
| 257 void forEach(void f(Element element, T type)) { | 143 void forEach(void f(Element element, TypeInformation type)) { |
| 258 if (fields == null) return; | 144 if (fields == null) return; |
| 259 fields.forEach(f); | 145 fields.forEach(f); |
| 260 } | 146 } |
| 261 | 147 |
| 262 void mergeDiamondFlow(FieldInitializationScope<T> thenScope, | 148 void mergeDiamondFlow( |
| 263 FieldInitializationScope<T> elseScope) { | 149 FieldInitializationScope thenScope, FieldInitializationScope elseScope) { |
| 264 // Quick bailout check. If [isThisExposed] is true, we know the | 150 // Quick bailout check. If [isThisExposed] is true, we know the |
| 265 // code following won't do anything. | 151 // code following won'TypeInformation do anything. |
| 266 if (isThisExposed) return; | 152 if (isThisExposed) return; |
| 267 if (elseScope == null || elseScope.fields == null) { | 153 if (elseScope == null || elseScope.fields == null) { |
| 268 elseScope = this; | 154 elseScope = this; |
| 269 } | 155 } |
| 270 | 156 |
| 271 thenScope.forEach((Element field, T type) { | 157 thenScope.forEach((Element field, TypeInformation type) { |
| 272 T otherType = elseScope.readField(field); | 158 TypeInformation otherType = elseScope.readField(field); |
| 273 if (otherType == null) return; | 159 if (otherType == null) return; |
| 274 updateField(field, types.allocateDiamondPhi(type, otherType)); | 160 updateField(field, types.allocateDiamondPhi(type, otherType)); |
| 275 }); | 161 }); |
| 276 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; | 162 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed; |
| 277 } | 163 } |
| 278 } | 164 } |
| 279 | 165 |
| 280 /** | 166 /** |
| 281 * Placeholder for inferred arguments types on sends. | 167 * Placeholder for inferred arguments types on sends. |
| 282 */ | 168 */ |
| 283 class ArgumentsTypes<T> extends IterableMixin<T> { | 169 class ArgumentsTypes extends IterableMixin<TypeInformation> { |
| 284 final List<T> positional; | 170 final List<TypeInformation> positional; |
| 285 final Map<String, T> named; | 171 final Map<String, TypeInformation> named; |
| 286 ArgumentsTypes(this.positional, named) | 172 ArgumentsTypes(this.positional, named) |
| 287 : this.named = (named == null || named.isEmpty) ? const {} : named { | 173 : this.named = (named == null || named.isEmpty) ? const {} : named { |
| 288 assert(this.positional.every((T type) => type != null)); | 174 assert(this.positional.every((TypeInformation type) => type != null)); |
| 289 assert(this.named.values.every((T type) => type != null)); | 175 assert(this.named.values.every((TypeInformation type) => type != null)); |
| 290 } | 176 } |
| 291 | 177 |
| 292 ArgumentsTypes.empty() | 178 ArgumentsTypes.empty() |
| 293 : positional = const [], | 179 : positional = const [], |
| 294 named = const {}; | 180 named = const {}; |
| 295 | 181 |
| 296 int get length => positional.length + named.length; | 182 int get length => positional.length + named.length; |
| 297 | 183 |
| 298 Iterator<T> get iterator => new ArgumentsTypesIterator(this); | 184 Iterator<TypeInformation> get iterator => new ArgumentsTypesIterator(this); |
| 299 | 185 |
| 300 String toString() => "{ positional = $positional, named = $named }"; | 186 String toString() => "{ positional = $positional, named = $named }"; |
| 301 | 187 |
| 302 bool operator ==(other) { | 188 bool operator ==(other) { |
| 303 if (positional.length != other.positional.length) return false; | 189 if (positional.length != other.positional.length) return false; |
| 304 if (named.length != other.named.length) return false; | 190 if (named.length != other.named.length) return false; |
| 305 for (int i = 0; i < positional.length; i++) { | 191 for (int i = 0; i < positional.length; i++) { |
| 306 if (positional[i] != other.positional[i]) return false; | 192 if (positional[i] != other.positional[i]) return false; |
| 307 } | 193 } |
| 308 named.forEach((name, type) { | 194 named.forEach((name, type) { |
| 309 if (other.named[name] != type) return false; | 195 if (other.named[name] != type) return false; |
| 310 }); | 196 }); |
| 311 return true; | 197 return true; |
| 312 } | 198 } |
| 313 | 199 |
| 314 int get hashCode => throw new UnsupportedError('ArgumentsTypes.hashCode'); | 200 int get hashCode => throw new UnsupportedError('ArgumentsTypes.hashCode'); |
| 315 | 201 |
| 316 bool hasNoArguments() => positional.isEmpty && named.isEmpty; | 202 bool hasNoArguments() => positional.isEmpty && named.isEmpty; |
| 317 | 203 |
| 318 void forEach(void f(T type)) { | 204 void forEach(void f(TypeInformation type)) { |
| 319 positional.forEach(f); | 205 positional.forEach(f); |
| 320 named.values.forEach(f); | 206 named.values.forEach(f); |
| 321 } | 207 } |
| 322 | 208 |
| 323 bool every(bool f(T type)) { | 209 bool every(bool f(TypeInformation type)) { |
| 324 return positional.every(f) && named.values.every(f); | 210 return positional.every(f) && named.values.every(f); |
| 325 } | 211 } |
| 326 | 212 |
| 327 bool contains(T type) { | 213 bool contains(TypeInformation type) { |
| 328 return positional.contains(type) || named.containsValue(type); | 214 return positional.contains(type) || named.containsValue(type); |
| 329 } | 215 } |
| 330 } | 216 } |
| 331 | 217 |
| 332 class ArgumentsTypesIterator<T> implements Iterator<T> { | 218 class ArgumentsTypesIterator implements Iterator<TypeInformation> { |
| 333 final Iterator<T> positional; | 219 final Iterator<TypeInformation> positional; |
| 334 final Iterator<T> named; | 220 final Iterator<TypeInformation> named; |
| 335 bool _iteratePositional = true; | 221 bool _iteratePositional = true; |
| 336 | 222 |
| 337 ArgumentsTypesIterator(ArgumentsTypes<T> iteratee) | 223 ArgumentsTypesIterator(ArgumentsTypes iteratee) |
| 338 : positional = iteratee.positional.iterator, | 224 : positional = iteratee.positional.iterator, |
| 339 named = iteratee.named.values.iterator; | 225 named = iteratee.named.values.iterator; |
| 340 | 226 |
| 341 Iterator<T> get _currentIterator => _iteratePositional ? positional : named; | 227 Iterator<TypeInformation> get _currentIterator => |
| 228 _iteratePositional ? positional : named; |
| 342 | 229 |
| 343 T get current => _currentIterator.current; | 230 TypeInformation get current => _currentIterator.current; |
| 344 | 231 |
| 345 bool moveNext() { | 232 bool moveNext() { |
| 346 if (_iteratePositional && positional.moveNext()) { | 233 if (_iteratePositional && positional.moveNext()) { |
| 347 return true; | 234 return true; |
| 348 } | 235 } |
| 349 _iteratePositional = false; | 236 _iteratePositional = false; |
| 350 return named.moveNext(); | 237 return named.moveNext(); |
| 351 } | 238 } |
| 352 } | 239 } |
| 353 | 240 |
| 354 abstract class MinimalInferrerEngine<T> { | |
| 355 /** | |
| 356 * Returns the type of [element]. | |
| 357 */ | |
| 358 T typeOfElement(Element element); | |
| 359 | |
| 360 /** | |
| 361 * Records that [node] sets non-final field [element] to be of type | |
| 362 * [type]. | |
| 363 */ | |
| 364 void recordTypeOfNonFinalField(Node node, Element field, T type); | |
| 365 | |
| 366 /** | |
| 367 * Records that the captured variable [local] is read. | |
| 368 */ | |
| 369 void recordCapturedLocalRead(Local local); | |
| 370 | |
| 371 /** | |
| 372 * Records that the variable [local] is being updated. | |
| 373 */ | |
| 374 void recordLocalUpdate(Local local, T type); | |
| 375 | |
| 376 /// The [ClosedWorld] on which inference reasoning is based. | |
| 377 ClosedWorld get closedWorld; | |
| 378 } | |
| 379 | |
| 380 /** | 241 /** |
| 381 * Placeholder for inferred types of local variables. | 242 * Placeholder for inferred types of local variables. |
| 382 */ | 243 */ |
| 383 class LocalsHandler<T> { | 244 class LocalsHandler { |
| 384 final CompilerOptions options; | 245 final CompilerOptions options; |
| 385 final TypeSystem<T> types; | 246 final TypeSystem types; |
| 386 final MinimalInferrerEngine<T> inferrer; | 247 final InferrerEngine inferrer; |
| 387 final VariableScope<T> locals; | 248 final VariableScope locals; |
| 388 final Map<Local, Element> captured; | 249 final Map<Local, Element> captured; |
| 389 final Map<Local, Element> capturedAndBoxed; | 250 final Map<Local, Element> capturedAndBoxed; |
| 390 final FieldInitializationScope<T> fieldScope; | 251 final FieldInitializationScope fieldScope; |
| 391 LocalsHandler<T> tryBlock; | 252 LocalsHandler tryBlock; |
| 392 bool seenReturnOrThrow = false; | 253 bool seenReturnOrThrow = false; |
| 393 bool seenBreakOrContinue = false; | 254 bool seenBreakOrContinue = false; |
| 394 | 255 |
| 395 bool get aborts { | 256 bool get aborts { |
| 396 return seenReturnOrThrow || seenBreakOrContinue; | 257 return seenReturnOrThrow || seenBreakOrContinue; |
| 397 } | 258 } |
| 398 | 259 |
| 399 bool get inTryBlock => tryBlock != null; | 260 bool get inTryBlock => tryBlock != null; |
| 400 | 261 |
| 401 LocalsHandler(this.inferrer, this.types, this.options, Node block, | 262 LocalsHandler(this.inferrer, this.types, this.options, Node block, |
| 402 [this.fieldScope]) | 263 [this.fieldScope]) |
| 403 : locals = new VariableScope<T>(block), | 264 : locals = new VariableScope(block), |
| 404 captured = new Map<Local, Element>(), | 265 captured = new Map<Local, Element>(), |
| 405 capturedAndBoxed = new Map<Local, Element>(), | 266 capturedAndBoxed = new Map<Local, Element>(), |
| 406 tryBlock = null; | 267 tryBlock = null; |
| 407 | 268 |
| 408 LocalsHandler.from(LocalsHandler<T> other, Node block, | 269 LocalsHandler.from(LocalsHandler other, Node block, |
| 409 {bool useOtherTryBlock: true}) | 270 {bool useOtherTryBlock: true}) |
| 410 : locals = new VariableScope<T>(block, other.locals), | 271 : locals = new VariableScope(block, other.locals), |
| 411 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), | 272 fieldScope = new FieldInitializationScope.from(other.fieldScope), |
| 412 captured = other.captured, | 273 captured = other.captured, |
| 413 capturedAndBoxed = other.capturedAndBoxed, | 274 capturedAndBoxed = other.capturedAndBoxed, |
| 414 types = other.types, | 275 types = other.types, |
| 415 inferrer = other.inferrer, | 276 inferrer = other.inferrer, |
| 416 options = other.options { | 277 options = other.options { |
| 417 tryBlock = useOtherTryBlock ? other.tryBlock : this; | 278 tryBlock = useOtherTryBlock ? other.tryBlock : this; |
| 418 } | 279 } |
| 419 | 280 |
| 420 LocalsHandler.deepCopyOf(LocalsHandler<T> other) | 281 LocalsHandler.deepCopyOf(LocalsHandler other) |
| 421 : locals = new VariableScope<T>.deepCopyOf(other.locals), | 282 : locals = new VariableScope.deepCopyOf(other.locals), |
| 422 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), | 283 fieldScope = new FieldInitializationScope.from(other.fieldScope), |
| 423 captured = other.captured, | 284 captured = other.captured, |
| 424 capturedAndBoxed = other.capturedAndBoxed, | 285 capturedAndBoxed = other.capturedAndBoxed, |
| 425 tryBlock = other.tryBlock, | 286 tryBlock = other.tryBlock, |
| 426 types = other.types, | 287 types = other.types, |
| 427 inferrer = other.inferrer, | 288 inferrer = other.inferrer, |
| 428 options = other.options; | 289 options = other.options; |
| 429 | 290 |
| 430 LocalsHandler.topLevelCopyOf(LocalsHandler<T> other) | 291 LocalsHandler.topLevelCopyOf(LocalsHandler other) |
| 431 : locals = new VariableScope<T>.topLevelCopyOf(other.locals), | 292 : locals = new VariableScope.topLevelCopyOf(other.locals), |
| 432 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope), | 293 fieldScope = new FieldInitializationScope.from(other.fieldScope), |
| 433 captured = other.captured, | 294 captured = other.captured, |
| 434 capturedAndBoxed = other.capturedAndBoxed, | 295 capturedAndBoxed = other.capturedAndBoxed, |
| 435 tryBlock = other.tryBlock, | 296 tryBlock = other.tryBlock, |
| 436 types = other.types, | 297 types = other.types, |
| 437 inferrer = other.inferrer, | 298 inferrer = other.inferrer, |
| 438 options = other.options; | 299 options = other.options; |
| 439 | 300 |
| 440 T use(Local local) { | 301 TypeInformation use(Local local) { |
| 441 if (capturedAndBoxed.containsKey(local)) { | 302 if (capturedAndBoxed.containsKey(local)) { |
| 442 return inferrer.typeOfElement(capturedAndBoxed[local]); | 303 return inferrer.typeOfElement(capturedAndBoxed[local]); |
| 443 } else { | 304 } else { |
| 444 if (captured.containsKey(local)) { | 305 if (captured.containsKey(local)) { |
| 445 inferrer.recordCapturedLocalRead(local); | 306 inferrer.recordCapturedLocalRead(local); |
| 446 } | 307 } |
| 447 return locals[local]; | 308 return locals[local]; |
| 448 } | 309 } |
| 449 } | 310 } |
| 450 | 311 |
| 451 void update(LocalElement local, T type, Node node) { | 312 void update(LocalElement local, TypeInformation type, Node node) { |
| 452 assert(type != null); | 313 assert(type != null); |
| 453 if (options.trustTypeAnnotations || options.enableTypeAssertions) { | 314 if (options.trustTypeAnnotations || options.enableTypeAssertions) { |
| 454 type = types.narrowType(type, local.type); | 315 type = types.narrowType(type, local.type); |
| 455 } | 316 } |
| 456 updateLocal() { | 317 updateLocal() { |
| 457 T currentType = locals[local]; | 318 TypeInformation currentType = locals[local]; |
| 458 | 319 |
| 459 SendSet send = node != null ? node.asSendSet() : null; | 320 SendSet send = node != null ? node.asSendSet() : null; |
| 460 if (send != null && send.isIfNullAssignment && currentType != null) { | 321 if (send != null && send.isIfNullAssignment && currentType != null) { |
| 461 // 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 |
| 462 // narrowed to non-null. | 323 // narrowed to non-null. |
| 463 type = types.addPhiInput( | 324 type = types.addPhiInput( |
| 464 local, | 325 local, |
| 465 types.allocatePhi( | 326 types.allocatePhi( |
| 466 locals.block, local, types.narrowNotNull(currentType)), | 327 locals.block, local, types.narrowNotNull(currentType)), |
| 467 type); | 328 type); |
| 468 } | 329 } |
| 469 locals[local] = type; | 330 locals[local] = type; |
| 470 if (currentType != type) { | 331 if (currentType != type) { |
| 471 inferrer.recordLocalUpdate(local, type); | 332 inferrer.recordLocalUpdate(local, type); |
| 472 } | 333 } |
| 473 } | 334 } |
| 474 | 335 |
| 475 if (capturedAndBoxed.containsKey(local)) { | 336 if (capturedAndBoxed.containsKey(local)) { |
| 476 inferrer.recordTypeOfNonFinalField(node, capturedAndBoxed[local], type); | 337 inferrer.recordTypeOfNonFinalField(node, capturedAndBoxed[local], type); |
| 477 } else if (inTryBlock) { | 338 } else if (inTryBlock) { |
| 478 // We don't know if an assignment in a try block | 339 // We don'TypeInformation know if an assignment in a try block |
| 479 // will be executed, so all assigments in that block are | 340 // will be executed, so all assigments in that block are |
| 480 // potential types after we have left it. We update the parent | 341 // potential types after we have left it. We update the parent |
| 481 // of the try block so that, at exit of the try block, we get | 342 // of the try block so that, at exit of the try block, we get |
| 482 // the right phi for it. | 343 // the right phi for it. |
| 483 T existing = tryBlock.locals.parent[local]; | 344 TypeInformation existing = tryBlock.locals.parent[local]; |
| 484 if (existing != null) { | 345 if (existing != null) { |
| 485 T phiType = types.allocatePhi(tryBlock.locals.block, local, existing); | 346 TypeInformation phiType = |
| 486 T inputType = types.addPhiInput(local, phiType, type); | 347 types.allocatePhi(tryBlock.locals.block, local, existing); |
| 348 TypeInformation inputType = types.addPhiInput(local, phiType, type); |
| 487 tryBlock.locals.parent[local] = inputType; | 349 tryBlock.locals.parent[local] = inputType; |
| 488 } | 350 } |
| 489 // Update the current handler unconditionnally with the new | 351 // Update the current handler unconditionnally with the new |
| 490 // type. | 352 // type. |
| 491 updateLocal(); | 353 updateLocal(); |
| 492 } else { | 354 } else { |
| 493 updateLocal(); | 355 updateLocal(); |
| 494 } | 356 } |
| 495 } | 357 } |
| 496 | 358 |
| 497 void setCaptured(Local local, Element field) { | 359 void setCaptured(Local local, Element field) { |
| 498 captured[local] = field; | 360 captured[local] = field; |
| 499 } | 361 } |
| 500 | 362 |
| 501 void setCapturedAndBoxed(Local local, Element field) { | 363 void setCapturedAndBoxed(Local local, Element field) { |
| 502 capturedAndBoxed[local] = field; | 364 capturedAndBoxed[local] = field; |
| 503 } | 365 } |
| 504 | 366 |
| 505 void mergeDiamondFlow( | 367 void mergeDiamondFlow(LocalsHandler thenBranch, LocalsHandler elseBranch) { |
| 506 LocalsHandler<T> thenBranch, LocalsHandler<T> elseBranch) { | |
| 507 if (fieldScope != null && elseBranch != null) { | 368 if (fieldScope != null && elseBranch != null) { |
| 508 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); | 369 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope); |
| 509 } | 370 } |
| 510 seenReturnOrThrow = thenBranch.seenReturnOrThrow && | 371 seenReturnOrThrow = thenBranch.seenReturnOrThrow && |
| 511 elseBranch != null && | 372 elseBranch != null && |
| 512 elseBranch.seenReturnOrThrow; | 373 elseBranch.seenReturnOrThrow; |
| 513 seenBreakOrContinue = thenBranch.seenBreakOrContinue && | 374 seenBreakOrContinue = thenBranch.seenBreakOrContinue && |
| 514 elseBranch != null && | 375 elseBranch != null && |
| 515 elseBranch.seenBreakOrContinue; | 376 elseBranch.seenBreakOrContinue; |
| 516 if (aborts) return; | 377 if (aborts) return; |
| 517 | 378 |
| 518 void mergeOneBranch(LocalsHandler<T> other) { | 379 void mergeOneBranch(LocalsHandler other) { |
| 519 other.locals.forEachOwnLocal((Local local, T type) { | 380 other.locals.forEachOwnLocal((Local local, TypeInformation type) { |
| 520 T myType = locals[local]; | 381 TypeInformation myType = locals[local]; |
| 521 if (myType == null) return; // Variable is only defined in [other]. | 382 if (myType == null) return; // Variable is only defined in [other]. |
| 522 if (type == myType) return; | 383 if (type == myType) return; |
| 523 locals[local] = types.allocateDiamondPhi(myType, type); | 384 locals[local] = types.allocateDiamondPhi(myType, type); |
| 524 }); | 385 }); |
| 525 } | 386 } |
| 526 | 387 |
| 527 void inPlaceUpdateOneBranch(LocalsHandler<T> other) { | 388 void inPlaceUpdateOneBranch(LocalsHandler other) { |
| 528 other.locals.forEachOwnLocal((Local local, T type) { | 389 other.locals.forEachOwnLocal((Local local, TypeInformation type) { |
| 529 T myType = locals[local]; | 390 TypeInformation myType = locals[local]; |
| 530 if (myType == null) return; // Variable is only defined in [other]. | 391 if (myType == null) return; // Variable is only defined in [other]. |
| 531 if (type == myType) return; | 392 if (type == myType) return; |
| 532 locals[local] = type; | 393 locals[local] = type; |
| 533 }); | 394 }); |
| 534 } | 395 } |
| 535 | 396 |
| 536 if (thenBranch.aborts) { | 397 if (thenBranch.aborts) { |
| 537 if (elseBranch == null) return; | 398 if (elseBranch == null) return; |
| 538 inPlaceUpdateOneBranch(elseBranch); | 399 inPlaceUpdateOneBranch(elseBranch); |
| 539 } else if (elseBranch == null) { | 400 } else if (elseBranch == null) { |
| 540 mergeOneBranch(thenBranch); | 401 mergeOneBranch(thenBranch); |
| 541 } else if (elseBranch.aborts) { | 402 } else if (elseBranch.aborts) { |
| 542 inPlaceUpdateOneBranch(thenBranch); | 403 inPlaceUpdateOneBranch(thenBranch); |
| 543 } else { | 404 } else { |
| 544 void mergeLocal(Local local) { | 405 void mergeLocal(Local local) { |
| 545 T myType = locals[local]; | 406 TypeInformation myType = locals[local]; |
| 546 if (myType == null) return; | 407 if (myType == null) return; |
| 547 T elseType = elseBranch.locals[local]; | 408 TypeInformation elseType = elseBranch.locals[local]; |
| 548 T thenType = thenBranch.locals[local]; | 409 TypeInformation thenType = thenBranch.locals[local]; |
| 549 if (thenType == elseType) { | 410 if (thenType == elseType) { |
| 550 locals[local] = thenType; | 411 locals[local] = thenType; |
| 551 } else { | 412 } else { |
| 552 locals[local] = types.allocateDiamondPhi(thenType, elseType); | 413 locals[local] = types.allocateDiamondPhi(thenType, elseType); |
| 553 } | 414 } |
| 554 } | 415 } |
| 555 | 416 |
| 556 thenBranch.locals.forEachOwnLocal((Local local, _) { | 417 thenBranch.locals.forEachOwnLocal((Local local, _) { |
| 557 mergeLocal(local); | 418 mergeLocal(local); |
| 558 }); | 419 }); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 587 * | 448 * |
| 588 * [: L: { | 449 * [: L: { |
| 589 * if (...) break; | 450 * if (...) break; |
| 590 * ... | 451 * ... |
| 591 * } | 452 * } |
| 592 * :] | 453 * :] |
| 593 * | 454 * |
| 594 * where [:this:] is the [LocalsHandler] for the paths through the | 455 * where [:this:] is the [LocalsHandler] for the paths through the |
| 595 * labeled statement that do not break out. | 456 * labeled statement that do not break out. |
| 596 */ | 457 */ |
| 597 void mergeAfterBreaks(List<LocalsHandler<T>> handlers, | 458 void mergeAfterBreaks(List<LocalsHandler> handlers, |
| 598 {bool keepOwnLocals: true}) { | 459 {bool keepOwnLocals: true}) { |
| 599 Node level = locals.block; | 460 Node level = locals.block; |
| 600 // Use a separate locals handler to perform the merge in, so that Phi | 461 // Use a separate locals handler to perform the merge in, so that Phi |
| 601 // creation does not invalidate previous type knowledge while we might | 462 // creation does not invalidate previous type knowledge while we might |
| 602 // still look it up. | 463 // still look it up. |
| 603 LocalsHandler merged = new LocalsHandler.from(this, level); | 464 LocalsHandler merged = new LocalsHandler.from(this, level); |
| 604 Set<Local> seenLocals = new Setlet<Local>(); | 465 Set<Local> seenLocals = new Setlet<Local>(); |
| 605 bool allBranchesAbort = true; | 466 bool allBranchesAbort = true; |
| 606 // Merge all other handlers. | 467 // Merge all other handlers. |
| 607 for (LocalsHandler handler in handlers) { | 468 for (LocalsHandler handler in handlers) { |
| 608 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; | 469 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow; |
| 609 merged.mergeHandler(handler, seenLocals); | 470 merged.mergeHandler(handler, seenLocals); |
| 610 } | 471 } |
| 611 // If we want to keep own locals, we merge [seenLocals] from [this] into | 472 // If we want to keep own locals, we merge [seenLocals] from [this] into |
| 612 // [merged] to update the Phi nodes with original values. | 473 // [merged] to update the Phi nodes with original values. |
| 613 if (keepOwnLocals && !seenReturnOrThrow) { | 474 if (keepOwnLocals && !seenReturnOrThrow) { |
| 614 for (Local variable in seenLocals) { | 475 for (Local variable in seenLocals) { |
| 615 T originalType = locals[variable]; | 476 TypeInformation originalType = locals[variable]; |
| 616 if (originalType != null) { | 477 if (originalType != null) { |
| 617 merged.locals[variable] = types.addPhiInput( | 478 merged.locals[variable] = types.addPhiInput( |
| 618 variable, merged.locals[variable], originalType); | 479 variable, merged.locals[variable], originalType); |
| 619 } | 480 } |
| 620 } | 481 } |
| 621 } | 482 } |
| 622 // Clean up Phi nodes with single input and store back result into | 483 // Clean up Phi nodes with single input and store back result into |
| 623 // actual locals handler. | 484 // actual locals handler. |
| 624 merged.locals.forEachOwnLocal((Local variable, T type) { | 485 merged.locals.forEachOwnLocal((Local variable, TypeInformation type) { |
| 625 locals[variable] = types.simplifyPhi(level, variable, type); | 486 locals[variable] = types.simplifyPhi(level, variable, type); |
| 626 }); | 487 }); |
| 627 seenReturnOrThrow = | 488 seenReturnOrThrow = |
| 628 allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); | 489 allBranchesAbort && (!keepOwnLocals || seenReturnOrThrow); |
| 629 } | 490 } |
| 630 | 491 |
| 631 /** | 492 /** |
| 632 * Merge [other] into this handler. Returns whether a local in this | 493 * Merge [other] into this handler. Returns whether a local in this |
| 633 * has changed. If [seen] is not null, we allocate new Phi nodes | 494 * has changed. If [seen] is not null, we allocate new Phi nodes |
| 634 * unless the local is already present in the set [seen]. This effectively | 495 * unless the local is already present in the set [seen]. This effectively |
| 635 * overwrites the current type knowledge in this handler. | 496 * overwrites the current type knowledge in this handler. |
| 636 */ | 497 */ |
| 637 bool mergeHandler(LocalsHandler<T> other, [Set<Local> seen]) { | 498 bool mergeHandler(LocalsHandler other, [Set<Local> seen]) { |
| 638 if (other.seenReturnOrThrow) return false; | 499 if (other.seenReturnOrThrow) return false; |
| 639 bool changed = false; | 500 bool changed = false; |
| 640 other.locals.forEachLocalUntilNode(locals.block, (local, otherType) { | 501 other.locals.forEachLocalUntilNode(locals.block, (local, otherType) { |
| 641 T myType = locals[local]; | 502 TypeInformation myType = locals[local]; |
| 642 if (myType == null) return; | 503 if (myType == null) return; |
| 643 T newType; | 504 TypeInformation newType; |
| 644 if (seen != null && !seen.contains(local)) { | 505 if (seen != null && !seen.contains(local)) { |
| 645 newType = types.allocatePhi(locals.block, local, otherType); | 506 newType = types.allocatePhi(locals.block, local, otherType); |
| 646 seen.add(local); | 507 seen.add(local); |
| 647 } else { | 508 } else { |
| 648 newType = types.addPhiInput(local, myType, otherType); | 509 newType = types.addPhiInput(local, myType, otherType); |
| 649 } | 510 } |
| 650 if (newType != myType) { | 511 if (newType != myType) { |
| 651 changed = true; | 512 changed = true; |
| 652 locals[local] = newType; | 513 locals[local] = newType; |
| 653 } | 514 } |
| 654 }); | 515 }); |
| 655 return changed; | 516 return changed; |
| 656 } | 517 } |
| 657 | 518 |
| 658 /** | 519 /** |
| 659 * Merge all [LocalsHandler] in [handlers] into this handler. | 520 * Merge all [LocalsHandler] in [handlers] into this handler. |
| 660 * Returns whether a local in this handler has changed. | 521 * Returns whether a local in this handler has changed. |
| 661 */ | 522 */ |
| 662 bool mergeAll(List<LocalsHandler<T>> handlers) { | 523 bool mergeAll(List<LocalsHandler> handlers) { |
| 663 bool changed = false; | 524 bool changed = false; |
| 664 assert(!seenReturnOrThrow); | 525 assert(!seenReturnOrThrow); |
| 665 handlers.forEach((other) { | 526 handlers.forEach((other) { |
| 666 changed = mergeHandler(other) || changed; | 527 changed = mergeHandler(other) || changed; |
| 667 }); | 528 }); |
| 668 return changed; | 529 return changed; |
| 669 } | 530 } |
| 670 | 531 |
| 671 void startLoop(Node loop) { | 532 void startLoop(Node loop) { |
| 672 locals.forEachLocal((Local variable, T type) { | 533 locals.forEachLocal((Local variable, TypeInformation type) { |
| 673 T newType = types.allocateLoopPhi(loop, variable, type); | 534 TypeInformation newType = types.allocateLoopPhi(loop, variable, type); |
| 674 if (newType != type) { | 535 if (newType != type) { |
| 675 locals[variable] = newType; | 536 locals[variable] = newType; |
| 676 } | 537 } |
| 677 }); | 538 }); |
| 678 } | 539 } |
| 679 | 540 |
| 680 void endLoop(Node loop) { | 541 void endLoop(Node loop) { |
| 681 locals.forEachLocal((Local variable, T type) { | 542 locals.forEachLocal((Local variable, TypeInformation type) { |
| 682 T newType = types.simplifyPhi(loop, variable, type); | 543 TypeInformation newType = types.simplifyPhi(loop, variable, type); |
| 683 if (newType != type) { | 544 if (newType != type) { |
| 684 locals[variable] = newType; | 545 locals[variable] = newType; |
| 685 } | 546 } |
| 686 }); | 547 }); |
| 687 } | 548 } |
| 688 | 549 |
| 689 void updateField(Element element, T type) { | 550 void updateField(Element element, TypeInformation type) { |
| 690 fieldScope.updateField(element, type); | 551 fieldScope.updateField(element, type); |
| 691 } | 552 } |
| 692 } | 553 } |
| 693 | |
| 694 abstract class InferrerVisitor<T, E extends MinimalInferrerEngine<T>> | |
| 695 extends Visitor<T> | |
| 696 with | |
| 697 SemanticSendResolvedMixin<T, dynamic>, | |
| 698 CompoundBulkMixin<T, dynamic>, | |
| 699 SetIfNullBulkMixin<T, dynamic>, | |
| 700 PrefixBulkMixin<T, dynamic>, | |
| 701 PostfixBulkMixin<T, dynamic>, | |
| 702 ErrorBulkMixin<T, dynamic>, | |
| 703 NewBulkMixin<T, dynamic>, | |
| 704 SetBulkMixin<T, dynamic> | |
| 705 implements SemanticSendVisitor<T, dynamic> { | |
| 706 final Compiler compiler; | |
| 707 final AstElement analyzedElement; | |
| 708 final ResolvedAst resolvedAst; | |
| 709 final TypeSystem<T> types; | |
| 710 final E inferrer; | |
| 711 final Map<JumpTarget, List<LocalsHandler<T>>> breaksFor = | |
| 712 new Map<JumpTarget, List<LocalsHandler<T>>>(); | |
| 713 final Map<JumpTarget, List<LocalsHandler>> continuesFor = | |
| 714 new Map<JumpTarget, List<LocalsHandler<T>>>(); | |
| 715 LocalsHandler<T> locals; | |
| 716 final List<T> cascadeReceiverStack = new List<T>(); | |
| 717 | |
| 718 TreeElements get elements => resolvedAst.elements; | |
| 719 | |
| 720 bool accumulateIsChecks = false; | |
| 721 bool conditionIsSimple = false; | |
| 722 List<Send> isChecks; | |
| 723 int loopLevel = 0; | |
| 724 | |
| 725 bool get inLoop => loopLevel > 0; | |
| 726 bool get isThisExposed { | |
| 727 return analyzedElement.isGenerativeConstructor | |
| 728 ? locals.fieldScope.isThisExposed | |
| 729 : true; | |
| 730 } | |
| 731 | |
| 732 void set isThisExposed(value) { | |
| 733 if (analyzedElement.isGenerativeConstructor) { | |
| 734 locals.fieldScope.isThisExposed = value; | |
| 735 } | |
| 736 } | |
| 737 | |
| 738 InferrerVisitor(AstElement analyzedElement, this.resolvedAst, this.inferrer, | |
| 739 this.types, this.compiler, | |
| 740 [LocalsHandler<T> handler]) | |
| 741 : this.analyzedElement = analyzedElement, | |
| 742 this.locals = handler { | |
| 743 if (handler != null) return; | |
| 744 Node node; | |
| 745 if (resolvedAst.kind == ResolvedAstKind.PARSED) { | |
| 746 node = resolvedAst.node; | |
| 747 } | |
| 748 FieldInitializationScope<T> fieldScope = | |
| 749 analyzedElement.isGenerativeConstructor | |
| 750 ? new FieldInitializationScope<T>(types) | |
| 751 : null; | |
| 752 locals = new LocalsHandler<T>( | |
| 753 inferrer, types, compiler.options, node, fieldScope); | |
| 754 } | |
| 755 | |
| 756 DiagnosticReporter get reporter => compiler.reporter; | |
| 757 | |
| 758 ClosedWorld get closedWorld => inferrer.closedWorld; | |
| 759 | |
| 760 @override | |
| 761 SemanticSendVisitor get sendVisitor => this; | |
| 762 | |
| 763 @override | |
| 764 T apply(Node node, _) => visit(node); | |
| 765 | |
| 766 T handleSendSet(SendSet node); | |
| 767 | |
| 768 T handleDynamicInvoke(Send node); | |
| 769 | |
| 770 T visitAssert(Assert node) { | |
| 771 // Avoid pollution from assert statement unless enabled. | |
| 772 if (!compiler.options.enableUserAssertions) { | |
| 773 return null; | |
| 774 } | |
| 775 List<Send> tests = <Send>[]; | |
| 776 bool simpleCondition = handleCondition(node.condition, tests); | |
| 777 LocalsHandler<T> saved = locals; | |
| 778 locals = new LocalsHandler<T>.from(locals, node); | |
| 779 updateIsChecks(tests, usePositive: true); | |
| 780 | |
| 781 LocalsHandler<T> thenLocals = locals; | |
| 782 locals = new LocalsHandler<T>.from(saved, node); | |
| 783 if (simpleCondition) updateIsChecks(tests, usePositive: false); | |
| 784 visit(node.message); | |
| 785 locals.seenReturnOrThrow = true; | |
| 786 saved.mergeDiamondFlow(thenLocals, locals); | |
| 787 locals = saved; | |
| 788 return null; | |
| 789 } | |
| 790 | |
| 791 T visitAsyncForIn(AsyncForIn node); | |
| 792 | |
| 793 T visitSyncForIn(SyncForIn node); | |
| 794 | |
| 795 T visitReturn(Return node); | |
| 796 | |
| 797 T visitFunctionExpression(FunctionExpression node); | |
| 798 | |
| 799 @override | |
| 800 T bulkHandleSet(SendSet node, _) { | |
| 801 return handleSendSet(node); | |
| 802 } | |
| 803 | |
| 804 @override | |
| 805 T bulkHandleCompound(SendSet node, _) { | |
| 806 return handleSendSet(node); | |
| 807 } | |
| 808 | |
| 809 @override | |
| 810 T bulkHandleSetIfNull(SendSet node, _) { | |
| 811 return handleSendSet(node); | |
| 812 } | |
| 813 | |
| 814 @override | |
| 815 T bulkHandlePrefix(SendSet node, _) { | |
| 816 return handleSendSet(node); | |
| 817 } | |
| 818 | |
| 819 @override | |
| 820 T bulkHandlePostfix(SendSet node, _) { | |
| 821 return handleSendSet(node); | |
| 822 } | |
| 823 | |
| 824 @override | |
| 825 T bulkHandleError(Node node, ErroneousElement error, _) { | |
| 826 return types.dynamicType; | |
| 827 } | |
| 828 | |
| 829 T visitNode(Node node) { | |
| 830 return node.visitChildren(this); | |
| 831 } | |
| 832 | |
| 833 T visit(Node node) { | |
| 834 return node == null ? null : node.accept(this); | |
| 835 } | |
| 836 | |
| 837 T visitFunctionDeclaration(FunctionDeclaration node) { | |
| 838 locals.update(elements[node], types.functionType, node); | |
| 839 return visit(node.function); | |
| 840 } | |
| 841 | |
| 842 T visitLiteralString(LiteralString node) { | |
| 843 return types.stringLiteralType(node.dartString); | |
| 844 } | |
| 845 | |
| 846 T visitStringInterpolation(StringInterpolation node) { | |
| 847 node.visitChildren(this); | |
| 848 return types.stringType; | |
| 849 } | |
| 850 | |
| 851 T visitStringJuxtaposition(StringJuxtaposition node) { | |
| 852 node.visitChildren(this); | |
| 853 return types.stringType; | |
| 854 } | |
| 855 | |
| 856 T visitLiteralBool(LiteralBool node) { | |
| 857 return types.boolLiteralType(node); | |
| 858 } | |
| 859 | |
| 860 T visitLiteralDouble(LiteralDouble node) { | |
| 861 ConstantSystem constantSystem = compiler.backend.constantSystem; | |
| 862 // The JavaScript backend may turn this literal into an integer at | |
| 863 // runtime. | |
| 864 return types.getConcreteTypeFor( | |
| 865 computeTypeMask(closedWorld, constantSystem.createDouble(node.value))); | |
| 866 } | |
| 867 | |
| 868 T visitLiteralInt(LiteralInt node) { | |
| 869 ConstantSystem constantSystem = compiler.backend.constantSystem; | |
| 870 // The JavaScript backend may turn this literal into a double at | |
| 871 // runtime. | |
| 872 return types.getConcreteTypeFor( | |
| 873 computeTypeMask(closedWorld, constantSystem.createInt(node.value))); | |
| 874 } | |
| 875 | |
| 876 T visitLiteralList(LiteralList node) { | |
| 877 node.visitChildren(this); | |
| 878 return node.isConst ? types.constListType : types.growableListType; | |
| 879 } | |
| 880 | |
| 881 T visitLiteralMap(LiteralMap node) { | |
| 882 node.visitChildren(this); | |
| 883 return node.isConst ? types.constMapType : types.mapType; | |
| 884 } | |
| 885 | |
| 886 T visitLiteralNull(LiteralNull node) { | |
| 887 return types.nullType; | |
| 888 } | |
| 889 | |
| 890 T visitLiteralSymbol(LiteralSymbol node) { | |
| 891 // TODO(kasperl): We should be able to tell that the type of a literal | |
| 892 // symbol is always a non-null exact symbol implementation -- not just | |
| 893 // any non-null subtype of the symbol interface. | |
| 894 return types.nonNullSubtype(closedWorld.commonElements.symbolClass); | |
| 895 } | |
| 896 | |
| 897 @override | |
| 898 void previsitDeferredAccess(Send node, PrefixElement prefix, _) { | |
| 899 // Deferred access does not affect inference. | |
| 900 } | |
| 901 | |
| 902 T handleTypeLiteralGet() { | |
| 903 return types.typeType; | |
| 904 } | |
| 905 | |
| 906 T handleTypeLiteralInvoke(NodeList arguments) { | |
| 907 return types.dynamicType; | |
| 908 } | |
| 909 | |
| 910 @override | |
| 911 T bulkHandleNode(Node node, String message, _) { | |
| 912 return internalError(node, message.replaceAll('#', '$node')); | |
| 913 } | |
| 914 | |
| 915 @override | |
| 916 T visitConstantGet(Send node, ConstantExpression constant, _) { | |
| 917 return bulkHandleNode(node, "Constant read `#` unhandled.", _); | |
| 918 } | |
| 919 | |
| 920 @override | |
| 921 T visitConstantInvoke(Send node, ConstantExpression constant, | |
| 922 NodeList arguments, CallStructure callStructure, _) { | |
| 923 return bulkHandleNode(node, "Constant invoke `#` unhandled.", _); | |
| 924 } | |
| 925 | |
| 926 T visitClassTypeLiteralGet(Send node, ConstantExpression constant, _) { | |
| 927 return handleTypeLiteralGet(); | |
| 928 } | |
| 929 | |
| 930 T visitClassTypeLiteralInvoke(Send node, ConstantExpression constant, | |
| 931 NodeList arguments, CallStructure callStructure, _) { | |
| 932 return handleTypeLiteralInvoke(arguments); | |
| 933 } | |
| 934 | |
| 935 T visitTypedefTypeLiteralGet(Send node, ConstantExpression constant, _) { | |
| 936 return handleTypeLiteralGet(); | |
| 937 } | |
| 938 | |
| 939 T visitTypedefTypeLiteralInvoke(Send node, ConstantExpression constant, | |
| 940 NodeList arguments, CallStructure callStructure, _) { | |
| 941 return handleTypeLiteralInvoke(arguments); | |
| 942 } | |
| 943 | |
| 944 T visitTypeVariableTypeLiteralGet(Send node, TypeVariableElement element, _) { | |
| 945 return handleTypeLiteralGet(); | |
| 946 } | |
| 947 | |
| 948 T visitTypeVariableTypeLiteralInvoke(Send node, TypeVariableElement element, | |
| 949 NodeList arguments, CallStructure callStructure, _) { | |
| 950 return handleTypeLiteralInvoke(arguments); | |
| 951 } | |
| 952 | |
| 953 T visitDynamicTypeLiteralGet(Send node, ConstantExpression constant, _) { | |
| 954 return handleTypeLiteralGet(); | |
| 955 } | |
| 956 | |
| 957 T visitDynamicTypeLiteralInvoke(Send node, ConstantExpression constant, | |
| 958 NodeList arguments, CallStructure callStructure, _) { | |
| 959 return handleTypeLiteralInvoke(arguments); | |
| 960 } | |
| 961 | |
| 962 bool isThisOrSuper(Node node) => node.isThis() || node.isSuper(); | |
| 963 | |
| 964 Element get outermostElement { | |
| 965 return analyzedElement.outermostEnclosingMemberOrTopLevel.implementation; | |
| 966 } | |
| 967 | |
| 968 T _thisType; | |
| 969 T get thisType { | |
| 970 if (_thisType != null) return _thisType; | |
| 971 ClassElement cls = outermostElement.enclosingClass; | |
| 972 if (closedWorld.isUsedAsMixin(cls)) { | |
| 973 return _thisType = types.nonNullSubtype(cls); | |
| 974 } else { | |
| 975 return _thisType = types.nonNullSubclass(cls); | |
| 976 } | |
| 977 } | |
| 978 | |
| 979 @override | |
| 980 T visitThisGet(Identifier node, _) { | |
| 981 return thisType; | |
| 982 } | |
| 983 | |
| 984 T visitIdentifier(Identifier node) { | |
| 985 if (node.isThis()) { | |
| 986 return thisType; | |
| 987 } else if (node.isSuper()) { | |
| 988 return internalError(node, 'Unexpected expression $node.'); | |
| 989 } else { | |
| 990 Element element = elements[node]; | |
| 991 if (Elements.isLocal(element)) { | |
| 992 LocalElement local = element; | |
| 993 return locals.use(local); | |
| 994 } | |
| 995 return null; | |
| 996 } | |
| 997 } | |
| 998 | |
| 999 void potentiallyAddIsCheck(Send node) { | |
| 1000 if (!accumulateIsChecks) return; | |
| 1001 if (!Elements.isLocal(elements[node.receiver])) return; | |
| 1002 isChecks.add(node); | |
| 1003 } | |
| 1004 | |
| 1005 void potentiallyAddNullCheck(Send node, Node receiver) { | |
| 1006 if (!accumulateIsChecks) return; | |
| 1007 if (!Elements.isLocal(elements[receiver])) return; | |
| 1008 isChecks.add(node); | |
| 1009 } | |
| 1010 | |
| 1011 void updateIsChecks(List<Node> tests, {bool usePositive}) { | |
| 1012 void narrow(Element element, ResolutionDartType type, Node node) { | |
| 1013 if (element is LocalElement) { | |
| 1014 T existing = locals.use(element); | |
| 1015 T newType = types.narrowType(existing, type, isNullable: false); | |
| 1016 locals.update(element, newType, node); | |
| 1017 } | |
| 1018 } | |
| 1019 | |
| 1020 if (tests == null) return; | |
| 1021 for (Send node in tests) { | |
| 1022 if (node.isTypeTest) { | |
| 1023 if (node.isIsNotCheck) { | |
| 1024 if (usePositive) continue; | |
| 1025 } else { | |
| 1026 if (!usePositive) continue; | |
| 1027 } | |
| 1028 ResolutionDartType type = | |
| 1029 elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
| 1030 narrow(elements[node.receiver], type, node); | |
| 1031 } else { | |
| 1032 Element receiverElement = elements[node.receiver]; | |
| 1033 Element argumentElement = elements[node.arguments.first]; | |
| 1034 String operator = node.selector.asOperator().source; | |
| 1035 if ((operator == '==' && usePositive) || | |
| 1036 (operator == '!=' && !usePositive)) { | |
| 1037 // Type the elements as null. | |
| 1038 if (Elements.isLocal(receiverElement)) { | |
| 1039 locals.update(receiverElement, types.nullType, node); | |
| 1040 } | |
| 1041 if (Elements.isLocal(argumentElement)) { | |
| 1042 locals.update(argumentElement, types.nullType, node); | |
| 1043 } | |
| 1044 } else { | |
| 1045 // Narrow the elements to a non-null type. | |
| 1046 ResolutionDartType objectType = closedWorld.commonElements.objectType; | |
| 1047 if (Elements.isLocal(receiverElement)) { | |
| 1048 narrow(receiverElement, objectType, node); | |
| 1049 } | |
| 1050 if (Elements.isLocal(argumentElement)) { | |
| 1051 narrow(argumentElement, objectType, node); | |
| 1052 } | |
| 1053 } | |
| 1054 } | |
| 1055 } | |
| 1056 } | |
| 1057 | |
| 1058 @override | |
| 1059 T visitIndex(Send node, Node receiver, Node index, _) { | |
| 1060 return handleDynamicInvoke(node); | |
| 1061 } | |
| 1062 | |
| 1063 @override | |
| 1064 T visitDynamicPropertyInvoke( | |
| 1065 Send node, Node receiver, NodeList arguments, Selector selector, _) { | |
| 1066 return handleDynamicInvoke(node); | |
| 1067 } | |
| 1068 | |
| 1069 @override | |
| 1070 T visitIfNotNullDynamicPropertyInvoke( | |
| 1071 Send node, Node receiver, NodeList arguments, Selector selector, _) { | |
| 1072 return handleDynamicInvoke(node); | |
| 1073 } | |
| 1074 | |
| 1075 @override | |
| 1076 T visitThisPropertyInvoke( | |
| 1077 Send node, NodeList arguments, Selector selector, _) { | |
| 1078 return handleDynamicInvoke(node); | |
| 1079 } | |
| 1080 | |
| 1081 @override | |
| 1082 T visitIfNull(Send node, Node left, Node right, _) { | |
| 1083 T firstType = visit(left); | |
| 1084 T secondType = visit(right); | |
| 1085 return types.allocateDiamondPhi(types.narrowNotNull(firstType), secondType); | |
| 1086 } | |
| 1087 | |
| 1088 @override | |
| 1089 T visitLogicalAnd(Send node, Node left, Node right, _) { | |
| 1090 conditionIsSimple = false; | |
| 1091 bool oldAccumulateIsChecks = accumulateIsChecks; | |
| 1092 List<Send> oldIsChecks = isChecks; | |
| 1093 if (!accumulateIsChecks) { | |
| 1094 accumulateIsChecks = true; | |
| 1095 isChecks = <Send>[]; | |
| 1096 } | |
| 1097 visit(left); | |
| 1098 LocalsHandler<T> saved = locals; | |
| 1099 locals = new LocalsHandler<T>.from(locals, node); | |
| 1100 updateIsChecks(isChecks, usePositive: true); | |
| 1101 LocalsHandler<T> narrowed; | |
| 1102 if (oldAccumulateIsChecks) { | |
| 1103 narrowed = new LocalsHandler<T>.topLevelCopyOf(locals); | |
| 1104 } else { | |
| 1105 accumulateIsChecks = false; | |
| 1106 isChecks = oldIsChecks; | |
| 1107 } | |
| 1108 visit(right); | |
| 1109 if (oldAccumulateIsChecks) { | |
| 1110 bool invalidatedInRightHandSide(Send test) { | |
| 1111 Element receiver = elements[test.receiver]; | |
| 1112 if (receiver is LocalElement) { | |
| 1113 return narrowed.locals[receiver] != locals.locals[receiver]; | |
| 1114 } | |
| 1115 return false; | |
| 1116 } | |
| 1117 | |
| 1118 isChecks.removeWhere(invalidatedInRightHandSide); | |
| 1119 } | |
| 1120 saved.mergeDiamondFlow(locals, null); | |
| 1121 locals = saved; | |
| 1122 return types.boolType; | |
| 1123 } | |
| 1124 | |
| 1125 @override | |
| 1126 T visitLogicalOr(Send node, Node left, Node right, _) { | |
| 1127 conditionIsSimple = false; | |
| 1128 List<Send> tests = <Send>[]; | |
| 1129 bool isSimple = handleCondition(left, tests); | |
| 1130 LocalsHandler<T> saved = locals; | |
| 1131 locals = new LocalsHandler<T>.from(locals, node); | |
| 1132 if (isSimple) updateIsChecks(tests, usePositive: false); | |
| 1133 bool oldAccumulateIsChecks = accumulateIsChecks; | |
| 1134 accumulateIsChecks = false; | |
| 1135 visit(right); | |
| 1136 accumulateIsChecks = oldAccumulateIsChecks; | |
| 1137 saved.mergeDiamondFlow(locals, null); | |
| 1138 locals = saved; | |
| 1139 return types.boolType; | |
| 1140 } | |
| 1141 | |
| 1142 @override | |
| 1143 T visitNot(Send node, Node expression, _) { | |
| 1144 bool oldAccumulateIsChecks = accumulateIsChecks; | |
| 1145 accumulateIsChecks = false; | |
| 1146 visit(expression); | |
| 1147 accumulateIsChecks = oldAccumulateIsChecks; | |
| 1148 return types.boolType; | |
| 1149 } | |
| 1150 | |
| 1151 @override | |
| 1152 T visitIs(Send node, Node expression, ResolutionDartType type, _) { | |
| 1153 potentiallyAddIsCheck(node); | |
| 1154 visit(expression); | |
| 1155 return types.boolType; | |
| 1156 } | |
| 1157 | |
| 1158 @override | |
| 1159 T visitIsNot(Send node, Node expression, ResolutionDartType type, _) { | |
| 1160 potentiallyAddIsCheck(node); | |
| 1161 visit(expression); | |
| 1162 return types.boolType; | |
| 1163 } | |
| 1164 | |
| 1165 @override | |
| 1166 T visitAs(Send node, Node expression, ResolutionDartType type, _) { | |
| 1167 T receiverType = visit(expression); | |
| 1168 return types.narrowType(receiverType, type); | |
| 1169 } | |
| 1170 | |
| 1171 @override | |
| 1172 T visitUnary(Send node, UnaryOperator operator, Node expression, _) { | |
| 1173 return handleDynamicInvoke(node); | |
| 1174 } | |
| 1175 | |
| 1176 @override | |
| 1177 T visitNotEquals(Send node, Node left, Node right, _) { | |
| 1178 handleDynamicInvoke(node); | |
| 1179 return types.boolType; | |
| 1180 } | |
| 1181 | |
| 1182 @override | |
| 1183 T visitEquals(Send node, Node left, Node right, _) { | |
| 1184 return handleDynamicInvoke(node); | |
| 1185 } | |
| 1186 | |
| 1187 @override | |
| 1188 T visitBinary(Send node, Node left, BinaryOperator operator, Node right, _) { | |
| 1189 return handleDynamicInvoke(node); | |
| 1190 } | |
| 1191 | |
| 1192 // Because some nodes just visit their children, we may end up | |
| 1193 // visiting a type annotation, that may contain a send in case of a | |
| 1194 // prefixed type. Therefore we explicitly visit the type annotation | |
| 1195 // to avoid confusing the [ResolvedVisitor]. | |
| 1196 visitTypeAnnotation(TypeAnnotation node) {} | |
| 1197 | |
| 1198 T visitConditional(Conditional node) { | |
| 1199 List<Send> tests = <Send>[]; | |
| 1200 bool simpleCondition = handleCondition(node.condition, tests); | |
| 1201 LocalsHandler<T> saved = locals; | |
| 1202 locals = new LocalsHandler<T>.from(locals, node); | |
| 1203 updateIsChecks(tests, usePositive: true); | |
| 1204 T firstType = visit(node.thenExpression); | |
| 1205 LocalsHandler<T> thenLocals = locals; | |
| 1206 locals = new LocalsHandler<T>.from(saved, node); | |
| 1207 if (simpleCondition) updateIsChecks(tests, usePositive: false); | |
| 1208 T secondType = visit(node.elseExpression); | |
| 1209 saved.mergeDiamondFlow(thenLocals, locals); | |
| 1210 locals = saved; | |
| 1211 T type = types.allocateDiamondPhi(firstType, secondType); | |
| 1212 return type; | |
| 1213 } | |
| 1214 | |
| 1215 T visitVariableDefinitions(VariableDefinitions node) { | |
| 1216 for (Link<Node> link = node.definitions.nodes; | |
| 1217 !link.isEmpty; | |
| 1218 link = link.tail) { | |
| 1219 Node definition = link.head; | |
| 1220 if (definition is Identifier) { | |
| 1221 locals.update(elements[definition], types.nullType, node); | |
| 1222 } else { | |
| 1223 assert(definition.asSendSet() != null); | |
| 1224 handleSendSet(definition); | |
| 1225 } | |
| 1226 } | |
| 1227 return null; | |
| 1228 } | |
| 1229 | |
| 1230 bool handleCondition(Node node, List<Send> tests) { | |
| 1231 bool oldConditionIsSimple = conditionIsSimple; | |
| 1232 bool oldAccumulateIsChecks = accumulateIsChecks; | |
| 1233 List<Send> oldIsChecks = isChecks; | |
| 1234 accumulateIsChecks = true; | |
| 1235 conditionIsSimple = true; | |
| 1236 isChecks = tests; | |
| 1237 visit(node); | |
| 1238 bool simpleCondition = conditionIsSimple; | |
| 1239 accumulateIsChecks = oldAccumulateIsChecks; | |
| 1240 isChecks = oldIsChecks; | |
| 1241 conditionIsSimple = oldConditionIsSimple; | |
| 1242 return simpleCondition; | |
| 1243 } | |
| 1244 | |
| 1245 T visitIf(If node) { | |
| 1246 List<Send> tests = <Send>[]; | |
| 1247 bool simpleCondition = handleCondition(node.condition, tests); | |
| 1248 LocalsHandler<T> saved = locals; | |
| 1249 locals = new LocalsHandler<T>.from(locals, node); | |
| 1250 updateIsChecks(tests, usePositive: true); | |
| 1251 visit(node.thenPart); | |
| 1252 LocalsHandler<T> thenLocals = locals; | |
| 1253 locals = new LocalsHandler<T>.from(saved, node); | |
| 1254 if (simpleCondition) updateIsChecks(tests, usePositive: false); | |
| 1255 visit(node.elsePart); | |
| 1256 saved.mergeDiamondFlow(thenLocals, locals); | |
| 1257 locals = saved; | |
| 1258 return null; | |
| 1259 } | |
| 1260 | |
| 1261 void setupBreaksAndContinues(JumpTarget element) { | |
| 1262 if (element == null) return; | |
| 1263 if (element.isContinueTarget) continuesFor[element] = <LocalsHandler>[]; | |
| 1264 if (element.isBreakTarget) breaksFor[element] = <LocalsHandler>[]; | |
| 1265 } | |
| 1266 | |
| 1267 void clearBreaksAndContinues(JumpTarget element) { | |
| 1268 continuesFor.remove(element); | |
| 1269 breaksFor.remove(element); | |
| 1270 } | |
| 1271 | |
| 1272 List<LocalsHandler<T>> getBreaks(JumpTarget element) { | |
| 1273 List<LocalsHandler<T>> list = <LocalsHandler<T>>[locals]; | |
| 1274 if (element == null) return list; | |
| 1275 if (!element.isBreakTarget) return list; | |
| 1276 return list..addAll(breaksFor[element]); | |
| 1277 } | |
| 1278 | |
| 1279 List<LocalsHandler<T>> getLoopBackEdges(JumpTarget element) { | |
| 1280 List<LocalsHandler<T>> list = <LocalsHandler<T>>[locals]; | |
| 1281 if (element == null) return list; | |
| 1282 if (!element.isContinueTarget) return list; | |
| 1283 return list..addAll(continuesFor[element]); | |
| 1284 } | |
| 1285 | |
| 1286 T handleLoop(Node node, void logic()) { | |
| 1287 loopLevel++; | |
| 1288 bool changed = false; | |
| 1289 JumpTarget target = elements.getTargetDefinition(node); | |
| 1290 LocalsHandler<T> saved = locals; | |
| 1291 saved.startLoop(node); | |
| 1292 do { | |
| 1293 // Setup (and clear in case of multiple iterations of the loop) | |
| 1294 // the lists of breaks and continues seen in the loop. | |
| 1295 setupBreaksAndContinues(target); | |
| 1296 locals = new LocalsHandler<T>.from(saved, node); | |
| 1297 logic(); | |
| 1298 changed = saved.mergeAll(getLoopBackEdges(target)); | |
| 1299 } while (changed); | |
| 1300 loopLevel--; | |
| 1301 saved.endLoop(node); | |
| 1302 bool keepOwnLocals = node.asDoWhile() == null; | |
| 1303 saved.mergeAfterBreaks(getBreaks(target), keepOwnLocals: keepOwnLocals); | |
| 1304 locals = saved; | |
| 1305 clearBreaksAndContinues(target); | |
| 1306 return null; | |
| 1307 } | |
| 1308 | |
| 1309 T visitWhile(While node) { | |
| 1310 return handleLoop(node, () { | |
| 1311 List<Send> tests = <Send>[]; | |
| 1312 handleCondition(node.condition, tests); | |
| 1313 updateIsChecks(tests, usePositive: true); | |
| 1314 visit(node.body); | |
| 1315 }); | |
| 1316 } | |
| 1317 | |
| 1318 T visitDoWhile(DoWhile node) { | |
| 1319 return handleLoop(node, () { | |
| 1320 visit(node.body); | |
| 1321 List<Send> tests = <Send>[]; | |
| 1322 handleCondition(node.condition, tests); | |
| 1323 updateIsChecks(tests, usePositive: true); | |
| 1324 }); | |
| 1325 } | |
| 1326 | |
| 1327 T visitFor(For node) { | |
| 1328 visit(node.initializer); | |
| 1329 return handleLoop(node, () { | |
| 1330 List<Send> tests = <Send>[]; | |
| 1331 handleCondition(node.condition, tests); | |
| 1332 updateIsChecks(tests, usePositive: true); | |
| 1333 visit(node.body); | |
| 1334 visit(node.update); | |
| 1335 }); | |
| 1336 } | |
| 1337 | |
| 1338 T visitTryStatement(TryStatement node) { | |
| 1339 LocalsHandler<T> saved = locals; | |
| 1340 locals = new LocalsHandler<T>.from(locals, node, useOtherTryBlock: false); | |
| 1341 visit(node.tryBlock); | |
| 1342 saved.mergeDiamondFlow(locals, null); | |
| 1343 locals = saved; | |
| 1344 for (Node catchBlock in node.catchBlocks) { | |
| 1345 saved = locals; | |
| 1346 locals = new LocalsHandler<T>.from(locals, catchBlock); | |
| 1347 visit(catchBlock); | |
| 1348 saved.mergeDiamondFlow(locals, null); | |
| 1349 locals = saved; | |
| 1350 } | |
| 1351 visit(node.finallyBlock); | |
| 1352 return null; | |
| 1353 } | |
| 1354 | |
| 1355 T visitThrow(Throw node) { | |
| 1356 node.visitChildren(this); | |
| 1357 locals.seenReturnOrThrow = true; | |
| 1358 return types.nonNullEmpty(); | |
| 1359 } | |
| 1360 | |
| 1361 T visitCatchBlock(CatchBlock node) { | |
| 1362 Node exception = node.exception; | |
| 1363 if (exception != null) { | |
| 1364 ResolutionDartType type = elements.getType(node.type); | |
| 1365 T mask = type == null || type.treatAsDynamic || type.isTypeVariable | |
| 1366 ? types.dynamicType | |
| 1367 : types.nonNullSubtype(type.element); | |
| 1368 locals.update(elements[exception], mask, node); | |
| 1369 } | |
| 1370 Node trace = node.trace; | |
| 1371 if (trace != null) { | |
| 1372 locals.update(elements[trace], types.dynamicType, node); | |
| 1373 } | |
| 1374 visit(node.block); | |
| 1375 return null; | |
| 1376 } | |
| 1377 | |
| 1378 T visitParenthesizedExpression(ParenthesizedExpression node) { | |
| 1379 return visit(node.expression); | |
| 1380 } | |
| 1381 | |
| 1382 T visitBlock(Block node) { | |
| 1383 if (node.statements != null) { | |
| 1384 for (Node statement in node.statements) { | |
| 1385 visit(statement); | |
| 1386 if (locals.aborts) break; | |
| 1387 } | |
| 1388 } | |
| 1389 return null; | |
| 1390 } | |
| 1391 | |
| 1392 T visitLabeledStatement(LabeledStatement node) { | |
| 1393 Statement body = node.statement; | |
| 1394 if (body is Loop || | |
| 1395 body is SwitchStatement || | |
| 1396 Elements.isUnusedLabel(node, elements)) { | |
| 1397 // Loops and switches handle their own labels. | |
| 1398 visit(body); | |
| 1399 } else { | |
| 1400 JumpTarget targetElement = elements.getTargetDefinition(body); | |
| 1401 setupBreaksAndContinues(targetElement); | |
| 1402 visit(body); | |
| 1403 locals.mergeAfterBreaks(getBreaks(targetElement)); | |
| 1404 clearBreaksAndContinues(targetElement); | |
| 1405 } | |
| 1406 return null; | |
| 1407 } | |
| 1408 | |
| 1409 T visitBreakStatement(BreakStatement node) { | |
| 1410 JumpTarget target = elements.getTargetOf(node); | |
| 1411 locals.seenBreakOrContinue = true; | |
| 1412 // Do a deep-copy of the locals, because the code following the | |
| 1413 // break will change them. | |
| 1414 breaksFor[target].add(new LocalsHandler<T>.deepCopyOf(locals)); | |
| 1415 return null; | |
| 1416 } | |
| 1417 | |
| 1418 T visitContinueStatement(ContinueStatement node) { | |
| 1419 JumpTarget target = elements.getTargetOf(node); | |
| 1420 locals.seenBreakOrContinue = true; | |
| 1421 // Do a deep-copy of the locals, because the code following the | |
| 1422 // continue will change them. | |
| 1423 continuesFor[target].add(new LocalsHandler<T>.deepCopyOf(locals)); | |
| 1424 return null; | |
| 1425 } | |
| 1426 | |
| 1427 internalError(Spannable node, String reason) { | |
| 1428 reporter.internalError(node, reason); | |
| 1429 } | |
| 1430 | |
| 1431 T visitSwitchStatement(SwitchStatement node) { | |
| 1432 visit(node.parenthesizedExpression); | |
| 1433 | |
| 1434 setupBreaksAndContinues(elements.getTargetDefinition(node)); | |
| 1435 if (Elements.switchStatementHasContinue(node, elements)) { | |
| 1436 void forEachLabeledCase(void action(JumpTarget target)) { | |
| 1437 for (SwitchCase switchCase in node.cases) { | |
| 1438 for (Node labelOrCase in switchCase.labelsAndCases) { | |
| 1439 if (labelOrCase.asLabel() == null) continue; | |
| 1440 LabelDefinition labelElement = | |
| 1441 elements.getLabelDefinition(labelOrCase); | |
| 1442 if (labelElement != null) { | |
| 1443 action(labelElement.target); | |
| 1444 } | |
| 1445 } | |
| 1446 } | |
| 1447 } | |
| 1448 | |
| 1449 forEachLabeledCase((JumpTarget target) { | |
| 1450 setupBreaksAndContinues(target); | |
| 1451 }); | |
| 1452 | |
| 1453 // If the switch statement has a continue, we conservatively | |
| 1454 // visit all cases and update [locals] until we have reached a | |
| 1455 // fixed point. | |
| 1456 bool changed; | |
| 1457 locals.startLoop(node); | |
| 1458 do { | |
| 1459 changed = false; | |
| 1460 for (Node switchCase in node.cases) { | |
| 1461 LocalsHandler<T> saved = locals; | |
| 1462 locals = new LocalsHandler<T>.from(locals, switchCase); | |
| 1463 visit(switchCase); | |
| 1464 changed = saved.mergeAll([locals]) || changed; | |
| 1465 locals = saved; | |
| 1466 } | |
| 1467 } while (changed); | |
| 1468 locals.endLoop(node); | |
| 1469 | |
| 1470 forEachLabeledCase((JumpTarget target) { | |
| 1471 clearBreaksAndContinues(target); | |
| 1472 }); | |
| 1473 } else { | |
| 1474 LocalsHandler<T> saved = locals; | |
| 1475 List<LocalsHandler<T>> localsToMerge = <LocalsHandler<T>>[]; | |
| 1476 bool hasDefaultCase = false; | |
| 1477 | |
| 1478 for (SwitchCase switchCase in node.cases) { | |
| 1479 if (switchCase.isDefaultCase) { | |
| 1480 hasDefaultCase = true; | |
| 1481 } | |
| 1482 locals = new LocalsHandler<T>.from(saved, switchCase); | |
| 1483 visit(switchCase); | |
| 1484 localsToMerge.add(locals); | |
| 1485 } | |
| 1486 saved.mergeAfterBreaks(localsToMerge, keepOwnLocals: !hasDefaultCase); | |
| 1487 locals = saved; | |
| 1488 } | |
| 1489 clearBreaksAndContinues(elements.getTargetDefinition(node)); | |
| 1490 return null; | |
| 1491 } | |
| 1492 | |
| 1493 T visitCascadeReceiver(CascadeReceiver node) { | |
| 1494 var type = visit(node.expression); | |
| 1495 cascadeReceiverStack.add(type); | |
| 1496 return type; | |
| 1497 } | |
| 1498 | |
| 1499 T visitCascade(Cascade node) { | |
| 1500 // Ignore the result of the cascade send and return the type of the cascade | |
| 1501 // receiver. | |
| 1502 visit(node.expression); | |
| 1503 return cascadeReceiverStack.removeLast(); | |
| 1504 } | |
| 1505 } | |
| OLD | NEW |