Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Side by Side Diff: pkg/compiler/lib/src/inferrer/inferrer_visitor.dart

Issue 2616103002: Flatten inferrer implementation (Closed)
Patch Set: Cleanup imports/prefixes Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698