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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/inferrer/inferrer_visitor.dart

Issue 692513002: Remove old dart2js code. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library inferrer_visitor;
6
7 import '../dart2jslib.dart' hide Selector, TypedSelector;
8 import '../dart_types.dart';
9 import '../elements/elements.dart';
10 import '../tree/tree.dart';
11 import '../universe/universe.dart';
12 import '../util/util.dart';
13 import '../types/types.dart' show TypeMask;
14 import 'dart:collection' show IterableMixin;
15
16 /**
17 * The interface [InferrerVisitor] will use when working on types.
18 */
19 abstract class TypeSystem<T> {
20 T get dynamicType;
21 T get nullType;
22 T get intType;
23 T get uint31Type;
24 T get uint32Type;
25 T get positiveIntType;
26 T get doubleType;
27 T get numType;
28 T get boolType;
29 T get functionType;
30 T get listType;
31 T get constListType;
32 T get fixedListType;
33 T get growableListType;
34 T get mapType;
35 T get constMapType;
36 T get stringType;
37 T get typeType;
38
39 T stringLiteralType(DartString value);
40
41 T nonNullSubtype(ClassElement type);
42 T nonNullSubclass(ClassElement type);
43 T nonNullExact(ClassElement type);
44 T nonNullEmpty();
45 bool isNull(T type);
46 Selector newTypedSelector(T receiver, Selector selector);
47
48 T allocateList(T type,
49 Node node,
50 Element enclosing,
51 [T elementType, int length]);
52
53 T allocateMap(T type, Node node, Element element, [List<T> keyType,
54 List<T> valueType]);
55
56 T allocateClosure(Node node, Element element);
57
58 /**
59 * Returns the least upper bound between [firstType] and
60 * [secondType].
61 */
62 T computeLUB(T firstType, T secondType);
63
64 /**
65 * Returns the intersection between [T] and [annotation].
66 * [isNullable] indicates whether the annotation implies a null
67 * type.
68 */
69 T narrowType(T type, DartType annotation, {bool isNullable: true});
70
71 /**
72 * Returns a new type that unions [firstInput] and [secondInput].
73 */
74 T allocateDiamondPhi(T firstInput, T secondInput);
75
76 /**
77 * Returns a new type for holding the potential types of [element].
78 * [inputType] is the first incoming type of the phi.
79 */
80 T allocatePhi(Node node, Local variable, T inputType);
81
82 /**
83 * Simplies the phi representing [element] and of the type
84 * [phiType]. For example, if this phi has one incoming input, an
85 * implementation of this method could just return that incoming
86 * input type.
87 */
88 T simplifyPhi(Node node, Local variable, T phiType);
89
90 /**
91 * Adds [newType] as an input of [phiType].
92 */
93 T addPhiInput(Local variable, T phiType, T newType);
94
95 /**
96 * Returns `true` if `selector` should be updated to reflect the new
97 * `receiverType`.
98 */
99 bool selectorNeedsUpdate(T receiverType, Selector selector);
100
101 /**
102 * Returns a new receiver type for this [selector] applied to
103 * [receiverType].
104 */
105 T refineReceiver(Selector selector, T receiverType);
106
107 /**
108 * Returns the internal inferrer representation for [mask].
109 */
110 T getConcreteTypeFor(TypeMask mask);
111 }
112
113 /**
114 * A variable scope holds types for variables. It has a link to a
115 * parent scope, but never changes the types in that parent. Instead,
116 * updates to locals of a parent scope are put in the current scope.
117 * The inferrer makes sure updates get merged into the parent scope,
118 * once the control flow block has been visited.
119 */
120 class VariableScope<T> {
121 Map<Local, T> variables;
122
123 /// The parent of this scope. Null for the root scope.
124 final VariableScope<T> parent;
125
126 /// The [Node] that created this scope.
127 final Node block;
128
129 VariableScope(this.block, [parent])
130 : this.variables = null,
131 this.parent = parent;
132
133 VariableScope.deepCopyOf(VariableScope<T> other)
134 : variables = other.variables == null
135 ? null
136 : new Map<Local, T>.from(other.variables),
137 block = other.block,
138 parent = other.parent == null
139 ? null
140 : new VariableScope<T>.deepCopyOf(other.parent);
141
142 T operator [](Local variable) {
143 T result;
144 if (variables == null || (result = variables[variable]) == null) {
145 return parent == null ? null : parent[variable];
146 }
147 return result;
148 }
149
150 void operator []=(Local variable, T mask) {
151 assert(mask != null);
152 if (variables == null) {
153 variables = new Map<Local, T>();
154 }
155 variables[variable] = mask;
156 }
157
158 void forEachOwnLocal(void f(Local variable, T type)) {
159 if (variables == null) return;
160 variables.forEach(f);
161 }
162
163 void forEachLocalUntilNode(Node node,
164 void f(Local variable, T type),
165 [Setlet<Local> seenLocals]) {
166 if (seenLocals == null) seenLocals = new Setlet<Local>();
167 if (variables != null) {
168 variables.forEach((variable, type) {
169 if (seenLocals.contains(variable)) return;
170 seenLocals.add(variable);
171 f(variable, type);
172 });
173 }
174 if (block == node) return;
175 if (parent != null) parent.forEachLocalUntilNode(node, f, seenLocals);
176 }
177
178 void forEachLocal(void f(Local variable, T type)) {
179 forEachLocalUntilNode(null, f);
180 }
181
182 bool updates(Local variable) {
183 if (variables == null) return false;
184 return variables.containsKey(variable);
185 }
186
187 String toString() {
188 String rest = parent == null ? "null" : parent.toString();
189 return '$variables $rest';
190 }
191 }
192
193 class FieldInitializationScope<T> {
194 final TypeSystem<T> types;
195 Map<Element, T> fields;
196 bool isThisExposed;
197
198 FieldInitializationScope(this.types) : isThisExposed = false;
199
200 FieldInitializationScope.internalFrom(FieldInitializationScope<T> other)
201 : types = other.types,
202 isThisExposed = other.isThisExposed;
203
204 factory FieldInitializationScope.from(FieldInitializationScope<T> other) {
205 if (other == null) return null;
206 return new FieldInitializationScope<T>.internalFrom(other);
207 }
208
209 void updateField(Element field, T type) {
210 if (isThisExposed) return;
211 if (fields == null) fields = new Map<Element, T>();
212 fields[field] = type;
213 }
214
215 T readField(Element field) {
216 return fields == null ? null : fields[field];
217 }
218
219 void forEach(void f(Element element, T type)) {
220 if (fields == null) return;
221 fields.forEach(f);
222 }
223
224 void mergeDiamondFlow(FieldInitializationScope<T> thenScope,
225 FieldInitializationScope<T> elseScope) {
226 // Quick bailout check. If [isThisExposed] is true, we know the
227 // code following won't do anything.
228 if (isThisExposed) return;
229 if (elseScope == null || elseScope.fields == null) {
230 elseScope = this;
231 }
232
233 thenScope.forEach((Element field, T type) {
234 T otherType = elseScope.readField(field);
235 if (otherType == null) return;
236 updateField(field, types.allocateDiamondPhi(type, otherType));
237 });
238 isThisExposed = thenScope.isThisExposed || elseScope.isThisExposed;
239 }
240 }
241
242 /**
243 * Placeholder for inferred arguments types on sends.
244 */
245 class ArgumentsTypes<T> extends IterableMixin<T> {
246 final List<T> positional;
247 final Map<String, T> named;
248 ArgumentsTypes(this.positional, named)
249 : this.named = (named == null || named.isEmpty) ? const {} : named {
250 assert(this.positional.every((T type) => type != null));
251 assert(this.named.values.every((T type) => type != null));
252 }
253
254 int get length => positional.length + named.length;
255
256 Iterator<T> get iterator => new ArgumentsTypesIterator(this);
257
258 String toString() => "{ positional = $positional, named = $named }";
259
260 bool operator==(other) {
261 if (positional.length != other.positional.length) return false;
262 if (named.length != other.named.length) return false;
263 for (int i = 0; i < positional.length; i++) {
264 if (positional[i] != other.positional[i]) return false;
265 }
266 named.forEach((name, type) {
267 if (other.named[name] != type) return false;
268 });
269 return true;
270 }
271
272 int get hashCode => throw new UnsupportedError('ArgumentsTypes.hashCode');
273
274 bool hasNoArguments() => positional.isEmpty && named.isEmpty;
275
276 bool hasOnePositionalArgumentThatMatches(bool f(T type)) {
277 return named.isEmpty && positional.length == 1 && f(positional[0]);
278 }
279
280 void forEach(void f(T type)) {
281 positional.forEach(f);
282 named.values.forEach(f);
283 }
284
285 bool every(bool f(T type)) {
286 return positional.every(f) && named.values.every(f);
287 }
288
289 bool contains(T type) {
290 return positional.contains(type) || named.containsValue(type);
291 }
292 }
293
294 class ArgumentsTypesIterator<T> implements Iterator<T> {
295 final Iterator<T> positional;
296 final Iterator<T> named;
297 bool _iteratePositional = true;
298
299 ArgumentsTypesIterator(ArgumentsTypes<T> iteratee)
300 : positional = iteratee.positional.iterator,
301 named = iteratee.named.values.iterator;
302
303 Iterator<T> get _currentIterator => _iteratePositional ? positional : named;
304
305 T get current => _currentIterator.current;
306
307 bool moveNext() {
308 if (_iteratePositional && positional.moveNext()) {
309 return true;
310 }
311 _iteratePositional = false;
312 return named.moveNext();
313 }
314 }
315
316
317 abstract class MinimalInferrerEngine<T> {
318 /**
319 * Returns the type of [element].
320 */
321 T typeOfElement(Element element);
322
323 /**
324 * Records that [node] sets non-final field [element] to be of type
325 * [type].
326 */
327 void recordTypeOfNonFinalField(Node node, Element field, T type);
328
329 /**
330 * Records that the captured variable [local] is read.
331 */
332 void recordCapturedLocalRead(Local local);
333
334 /**
335 * Records that the variable [local] is being updated.
336 */
337 void recordLocalUpdate(Local local, T type);
338 }
339
340 /**
341 * Placeholder for inferred types of local variables.
342 */
343 class LocalsHandler<T> {
344 final Compiler compiler;
345 final TypeSystem<T> types;
346 final MinimalInferrerEngine<T> inferrer;
347 final VariableScope<T> locals;
348 final Map<Local, Element> captured;
349 final Map<Local, Element> capturedAndBoxed;
350 final FieldInitializationScope<T> fieldScope;
351 LocalsHandler<T> tryBlock;
352 bool seenReturnOrThrow = false;
353 bool seenBreakOrContinue = false;
354
355 bool get aborts {
356 return seenReturnOrThrow || seenBreakOrContinue;
357 }
358 bool get inTryBlock => tryBlock != null;
359
360 LocalsHandler(this.inferrer,
361 this.types,
362 this.compiler,
363 Node block,
364 [this.fieldScope])
365 : locals = new VariableScope<T>(block),
366 captured = new Map<Local, Element>(),
367 capturedAndBoxed = new Map<Local, Element>(),
368 tryBlock = null;
369
370 LocalsHandler.from(LocalsHandler<T> other,
371 Node block,
372 {bool useOtherTryBlock: true})
373 : locals = new VariableScope<T>(block, other.locals),
374 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope),
375 captured = other.captured,
376 capturedAndBoxed = other.capturedAndBoxed,
377 types = other.types,
378 inferrer = other.inferrer,
379 compiler = other.compiler {
380 tryBlock = useOtherTryBlock ? other.tryBlock : this;
381 }
382
383 LocalsHandler.deepCopyOf(LocalsHandler<T> other)
384 : locals = new VariableScope<T>.deepCopyOf(other.locals),
385 fieldScope = new FieldInitializationScope<T>.from(other.fieldScope),
386 captured = other.captured,
387 capturedAndBoxed = other.capturedAndBoxed,
388 tryBlock = other.tryBlock,
389 types = other.types,
390 inferrer = other.inferrer,
391 compiler = other.compiler;
392
393 T use(Local local) {
394 if (capturedAndBoxed.containsKey(local)) {
395 return inferrer.typeOfElement(capturedAndBoxed[local]);
396 } else {
397 if (captured.containsKey(local)) {
398 inferrer.recordCapturedLocalRead(local);
399 }
400 return locals[local];
401 }
402 }
403
404 void update(LocalElement local, T type, Node node) {
405 assert(type != null);
406 if (compiler.trustTypeAnnotations || compiler.enableTypeAssertions) {
407 type = types.narrowType(type, local.type);
408 }
409 updateLocal() {
410 T currentType = locals[local];
411 locals[local] = type;
412 if (currentType != type) {
413 inferrer.recordLocalUpdate(local, type);
414 }
415 }
416 if (capturedAndBoxed.containsKey(local)) {
417 inferrer.recordTypeOfNonFinalField(
418 node, capturedAndBoxed[local], type);
419 } else if (inTryBlock) {
420 // We don't know if an assignment in a try block
421 // will be executed, so all assigments in that block are
422 // potential types after we have left it. We update the parent
423 // of the try block so that, at exit of the try block, we get
424 // the right phi for it.
425 T existing = tryBlock.locals.parent[local];
426 if (existing != null) {
427 T phiType = types.allocatePhi(tryBlock.locals.block, local, existing);
428 T inputType = types.addPhiInput(local, phiType, type);
429 tryBlock.locals.parent[local] = inputType;
430 }
431 // Update the current handler unconditionnally with the new
432 // type.
433 updateLocal();
434 } else {
435 updateLocal();
436 }
437 }
438
439 void setCaptured(Local local, Element field) {
440 captured[local] = field;
441 }
442
443 void setCapturedAndBoxed(Local local, Element field) {
444 capturedAndBoxed[local] = field;
445 }
446
447 void mergeDiamondFlow(LocalsHandler<T> thenBranch,
448 LocalsHandler<T> elseBranch) {
449 if (fieldScope != null && elseBranch != null) {
450 fieldScope.mergeDiamondFlow(thenBranch.fieldScope, elseBranch.fieldScope);
451 }
452 seenReturnOrThrow = thenBranch.seenReturnOrThrow
453 && elseBranch != null
454 && elseBranch.seenReturnOrThrow;
455 seenBreakOrContinue = thenBranch.seenBreakOrContinue
456 && elseBranch != null
457 && elseBranch.seenBreakOrContinue;
458 if (aborts) return;
459
460 void mergeOneBranch(LocalsHandler<T> other) {
461 other.locals.forEachOwnLocal((Local local, T type) {
462 T myType = locals[local];
463 if (myType == null) return; // Variable is only defined in [other].
464 if (type == myType) return;
465 locals[local] = types.allocateDiamondPhi(myType, type);
466 });
467 }
468
469 void inPlaceUpdateOneBranch(LocalsHandler<T> other) {
470 other.locals.forEachOwnLocal((Local local, T type) {
471 T myType = locals[local];
472 if (myType == null) return; // Variable is only defined in [other].
473 if (type == myType) return;
474 locals[local] = type;
475 });
476 }
477
478 if (thenBranch.aborts) {
479 if (elseBranch == null) return;
480 inPlaceUpdateOneBranch(elseBranch);
481 } else if (elseBranch == null) {
482 mergeOneBranch(thenBranch);
483 } else if (elseBranch.aborts) {
484 inPlaceUpdateOneBranch(thenBranch);
485 } else {
486 void mergeLocal(Local local) {
487 T myType = locals[local];
488 if (myType == null) return;
489 T elseType = elseBranch.locals[local];
490 T thenType = thenBranch.locals[local];
491 if (thenType == elseType) {
492 locals[local] = thenType;
493 } else {
494 locals[local] = types.allocateDiamondPhi(thenType, elseType);
495 }
496 }
497
498 thenBranch.locals.forEachOwnLocal((Local local, _) {
499 mergeLocal(local);
500 });
501 elseBranch.locals.forEachOwnLocal((Local local, _) {
502 // Discard locals we already processed when iterating over
503 // [thenBranch]'s locals.
504 if (!thenBranch.locals.updates(local)) mergeLocal(local);
505 });
506 }
507 }
508
509 /**
510 * Merge all [LocalsHandler] in [handlers] into [:this:].
511 *
512 * If [keepOwnLocals] is true, the types of locals in this
513 * [LocalsHandler] are being used in the merge. [keepOwnLocals]
514 * should be true if this [LocalsHandler], the dominator of
515 * all [handlers], also direclty flows into the join point,
516 * that is the code after all [handlers]. For example, consider:
517 *
518 * [: switch (...) {
519 * case 1: ...; break;
520 * }
521 * :]
522 *
523 * The [LocalsHandler] at entry of the switch also flows into the
524 * exit of the switch, because there is no default case. So the
525 * types of locals at entry of the switch have to take part to the
526 * merge.
527 *
528 * The above situation is also true for labeled statements like
529 *
530 * [: L: {
531 * if (...) break;
532 * ...
533 * }
534 * :]
535 *
536 * where [:this:] is the [LocalsHandler] for the paths through the
537 * labeled statement that do not break out.
538 */
539 void mergeAfterBreaks(List<LocalsHandler<T>> handlers,
540 {bool keepOwnLocals: true}) {
541 Node level = locals.block;
542 Set<Local> seenLocals = new Setlet<Local>();
543 // If we want to keep the locals, we first merge [this] into itself to
544 // create the required Phi nodes.
545 if (keepOwnLocals && !seenReturnOrThrow) {
546 mergeHandler(this, seenLocals);
547 }
548 bool allBranchesAbort = true;
549 // Merge all other handlers.
550 for (LocalsHandler handler in handlers) {
551 allBranchesAbort = allBranchesAbort && handler.seenReturnOrThrow;
552 mergeHandler(handler, seenLocals);
553 }
554 // Clean up Phi nodes with single input.
555 locals.forEachLocal((Local variable, T type) {
556 if (!seenLocals.contains(variable)) return;
557 T newType = types.simplifyPhi(level, variable, type);
558 if (newType != type) {
559 locals[variable] = newType;
560 }
561 });
562 seenReturnOrThrow = allBranchesAbort &&
563 (!keepOwnLocals || seenReturnOrThrow);
564 }
565
566 /**
567 * Merge [other] into this handler. Returns whether a local in this
568 * has changed. If [seen] is not null, we allocate new Phi nodes
569 * unless the local is already present in the set [seen]. This effectively
570 * overwrites the current type knowledge in this handler.
571 */
572 bool mergeHandler(LocalsHandler<T> other, [Set<Local> seen]) {
573 if (other.seenReturnOrThrow) return false;
574 bool changed = false;
575 other.locals.forEachLocalUntilNode(locals.block, (local, otherType) {
576 T myType = locals[local];
577 if (myType == null) return;
578 T newType;
579 if (seen != null && !seen.contains(local)) {
580 newType = types.allocatePhi(locals.block, local, otherType);
581 seen.add(local);
582 } else {
583 newType = types.addPhiInput(local, myType, otherType);
584 }
585 if (newType != myType) {
586 changed = true;
587 locals[local] = newType;
588 }
589 });
590 return changed;
591 }
592
593 /**
594 * Merge all [LocalsHandler] in [handlers] into this handler.
595 * Returns whether a local in this handler has changed.
596 */
597 bool mergeAll(List<LocalsHandler<T>> handlers) {
598 bool changed = false;
599 assert(!seenReturnOrThrow);
600 handlers.forEach((other) {
601 changed = mergeHandler(other) || changed;
602 });
603 return changed;
604 }
605
606 void startLoop(Node loop) {
607 locals.forEachLocal((Local variable, T type) {
608 T newType = types.allocatePhi(loop, variable, type);
609 if (newType != type) {
610 locals[variable] = newType;
611 }
612 });
613 }
614
615 void endLoop(Node loop) {
616 locals.forEachLocal((Local variable, T type) {
617 T newType = types.simplifyPhi(loop, variable, type);
618 if (newType != type) {
619 locals[variable] = newType;
620 }
621 });
622 }
623
624 void updateField(Element element, T type) {
625 fieldScope.updateField(element, type);
626 }
627 }
628
629 abstract class InferrerVisitor
630 <T, E extends MinimalInferrerEngine<T>> extends ResolvedVisitor<T> {
631 final Compiler compiler;
632 final AstElement analyzedElement;
633 final TypeSystem<T> types;
634 final E inferrer;
635 final Map<JumpTarget, List<LocalsHandler<T>>> breaksFor =
636 new Map<JumpTarget, List<LocalsHandler<T>>>();
637 final Map<JumpTarget, List<LocalsHandler>> continuesFor =
638 new Map<JumpTarget, List<LocalsHandler<T>>>();
639 LocalsHandler<T> locals;
640 final List<T> cascadeReceiverStack = new List<T>();
641
642 bool accumulateIsChecks = false;
643 bool conditionIsSimple = false;
644 List<Send> isChecks;
645 int loopLevel = 0;
646
647 bool get inLoop => loopLevel > 0;
648 bool get isThisExposed {
649 return analyzedElement.isGenerativeConstructor
650 ? locals.fieldScope.isThisExposed
651 : true;
652 }
653 void set isThisExposed(value) {
654 if (analyzedElement.isGenerativeConstructor) {
655 locals.fieldScope.isThisExposed = value;
656 }
657 }
658
659 InferrerVisitor(AstElement analyzedElement,
660 this.inferrer,
661 this.types,
662 this.compiler,
663 [LocalsHandler<T> handler])
664 : this.analyzedElement = analyzedElement,
665 this.locals = handler,
666 super(analyzedElement.resolvedAst.elements) {
667 if (handler != null) return;
668 Node node = analyzedElement.node;
669 FieldInitializationScope<T> fieldScope =
670 analyzedElement.isGenerativeConstructor
671 ? new FieldInitializationScope<T>(types)
672 : null;
673 locals = new LocalsHandler<T>(inferrer, types, compiler, node, fieldScope);
674 }
675
676 T visitSendSet(SendSet node);
677
678 T visitSuperSend(Send node);
679
680 T visitStaticSend(Send node);
681
682 T visitGetterSend(Send node);
683
684 T visitClosureSend(Send node);
685
686 T visitDynamicSend(Send node);
687
688 T visitForIn(ForIn node);
689
690 T visitReturn(Return node);
691
692 T visitFunctionExpression(FunctionExpression node);
693
694 T visitAssert(Send node) {
695 if (!compiler.enableUserAssertions) {
696 return types.nullType;
697 }
698 // TODO(johnniwinther): Don't handle assert like a regular static call since
699 // it break the selector name check.
700 return visitStaticSend(node);
701 }
702
703 T visitNode(Node node) {
704 return node.visitChildren(this);
705 }
706
707 T visitNewExpression(NewExpression node) {
708 return node.send.accept(this);
709 }
710
711 T visit(Node node) {
712 return node == null ? null : node.accept(this);
713 }
714
715 T visitFunctionDeclaration(FunctionDeclaration node) {
716 locals.update(elements[node], types.functionType, node);
717 return visit(node.function);
718 }
719
720 T visitLiteralString(LiteralString node) {
721 return types.stringLiteralType(node.dartString);
722 }
723
724 T visitStringInterpolation(StringInterpolation node) {
725 node.visitChildren(this);
726 return types.stringType;
727 }
728
729 T visitStringJuxtaposition(StringJuxtaposition node) {
730 node.visitChildren(this);
731 return types.stringType;
732 }
733
734 T visitLiteralBool(LiteralBool node) {
735 return types.boolType;
736 }
737
738 T visitLiteralDouble(LiteralDouble node) {
739 ConstantSystem constantSystem = compiler.backend.constantSystem;
740 // The JavaScript backend may turn this literal into an integer at
741 // runtime.
742 return types.getConcreteTypeFor(
743 constantSystem.createDouble(node.value).computeMask(compiler));
744 }
745
746 T visitLiteralInt(LiteralInt node) {
747 ConstantSystem constantSystem = compiler.backend.constantSystem;
748 // The JavaScript backend may turn this literal into a double at
749 // runtime.
750 return types.getConcreteTypeFor(
751 constantSystem.createInt(node.value).computeMask(compiler));
752 }
753
754 T visitLiteralList(LiteralList node) {
755 node.visitChildren(this);
756 return node.isConst ? types.constListType : types.growableListType;
757 }
758
759 T visitLiteralMap(LiteralMap node) {
760 node.visitChildren(this);
761 return node.isConst ? types.constMapType : types.mapType;
762 }
763
764 T visitLiteralNull(LiteralNull node) {
765 return types.nullType;
766 }
767
768 T visitLiteralSymbol(LiteralSymbol node) {
769 // TODO(kasperl): We should be able to tell that the type of a literal
770 // symbol is always a non-null exact symbol implementation -- not just
771 // any non-null subtype of the symbol interface.
772 return types.nonNullSubtype(compiler.symbolClass);
773 }
774
775 T visitTypePrefixSend(Send node) {
776 // TODO(johnniwinther): Remove the need for handling this node.
777 return types.dynamicType;
778 }
779
780 T visitTypeLiteralSend(Send node) {
781 return types.typeType;
782 }
783
784 bool isThisOrSuper(Node node) => node.isThis() || node.isSuper();
785
786 Element get outermostElement {
787 return analyzedElement.outermostEnclosingMemberOrTopLevel.implementation;
788 }
789
790 T _thisType;
791 T get thisType {
792 if (_thisType != null) return _thisType;
793 ClassElement cls = outermostElement.enclosingClass;
794 ClassWorld classWorld = compiler.world;
795 if (classWorld.isUsedAsMixin(cls)) {
796 return _thisType = types.nonNullSubtype(cls);
797 } else {
798 return _thisType = types.nonNullSubclass(cls);
799 }
800 }
801
802 T _superType;
803 T get superType {
804 if (_superType != null) return _superType;
805 return _superType = types.nonNullExact(
806 outermostElement.enclosingClass.superclass);
807 }
808
809 T visitIdentifier(Identifier node) {
810 if (node.isThis()) {
811 return thisType;
812 } else if (node.isSuper()) {
813 return superType;
814 } else {
815 Element element = elements[node];
816 if (Elements.isLocal(element)) {
817 LocalElement local = element;
818 return locals.use(local);
819 }
820 return null;
821 }
822 }
823
824 void potentiallyAddIsCheck(Send node) {
825 if (!accumulateIsChecks) return;
826 if (!Elements.isLocal(elements[node.receiver])) return;
827 isChecks.add(node);
828 }
829
830 void potentiallyAddNullCheck(Send node, Node receiver) {
831 if (!accumulateIsChecks) return;
832 if (!Elements.isLocal(elements[receiver])) return;
833 isChecks.add(node);
834 }
835
836 void updateIsChecks(List<Node> tests, {bool usePositive}) {
837 void narrow(Element element, DartType type, Node node) {
838 if (element is LocalElement) {
839 T existing = locals.use(element);
840 T newType = types.narrowType(existing, type, isNullable: false);
841 locals.update(element, newType, node);
842 }
843 }
844
845 if (tests == null) return;
846 for (Send node in tests) {
847 if (node.isTypeTest) {
848 if (node.isIsNotCheck) {
849 if (usePositive) continue;
850 } else {
851 if (!usePositive) continue;
852 }
853 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
854 narrow(elements[node.receiver], type, node);
855 } else {
856 Element receiverElement = elements[node.receiver];
857 Element argumentElement = elements[node.arguments.first];
858 String operator = node.selector.asOperator().source;
859 if ((operator == '==' && usePositive)
860 || (operator == '!=' && !usePositive)) {
861 // Type the elements as null.
862 if (Elements.isLocal(receiverElement)) {
863 locals.update(receiverElement, types.nullType, node);
864 }
865 if (Elements.isLocal(argumentElement)) {
866 locals.update(argumentElement, types.nullType, node);
867 }
868 } else {
869 // Narrow the elements to a non-null type.
870 DartType objectType = compiler.objectClass.rawType;
871 if (Elements.isLocal(receiverElement)) {
872 narrow(receiverElement, objectType, node);
873 }
874 if (Elements.isLocal(argumentElement)) {
875 narrow(argumentElement, objectType, node);
876 }
877 }
878 }
879 }
880 }
881
882 T visitOperatorSend(Send node) {
883 Operator op = node.selector;
884 if ("[]" == op.source) {
885 return visitDynamicSend(node);
886 } else if ("&&" == op.source) {
887 conditionIsSimple = false;
888 bool oldAccumulateIsChecks = accumulateIsChecks;
889 List<Send> oldIsChecks = isChecks;
890 if (!accumulateIsChecks) {
891 accumulateIsChecks = true;
892 isChecks = <Send>[];
893 }
894 visit(node.receiver);
895 LocalsHandler<T> saved = locals;
896 locals = new LocalsHandler<T>.from(locals, node);
897 updateIsChecks(isChecks, usePositive: true);
898 if (!oldAccumulateIsChecks) {
899 accumulateIsChecks = false;
900 isChecks = oldIsChecks;
901 }
902 visit(node.arguments.head);
903 saved.mergeDiamondFlow(locals, null);
904 locals = saved;
905 return types.boolType;
906 } else if ("||" == op.source) {
907 conditionIsSimple = false;
908 List<Send> tests = <Send>[];
909 bool isSimple = handleCondition(node.receiver, tests);
910 LocalsHandler<T> saved = locals;
911 locals = new LocalsHandler<T>.from(locals, node);
912 if (isSimple) updateIsChecks(tests, usePositive: false);
913 bool oldAccumulateIsChecks = accumulateIsChecks;
914 accumulateIsChecks = false;
915 visit(node.arguments.head);
916 accumulateIsChecks = oldAccumulateIsChecks;
917 saved.mergeDiamondFlow(locals, null);
918 locals = saved;
919 return types.boolType;
920 } else if ("!" == op.source) {
921 bool oldAccumulateIsChecks = accumulateIsChecks;
922 accumulateIsChecks = false;
923 node.visitChildren(this);
924 accumulateIsChecks = oldAccumulateIsChecks;
925 return types.boolType;
926 } else if ("is" == op.source) {
927 potentiallyAddIsCheck(node);
928 node.visitChildren(this);
929 return types.boolType;
930 } else if ("as" == op.source) {
931 T receiverType = visit(node.receiver);
932 DartType type = elements.getType(node.arguments.head);
933 return types.narrowType(receiverType, type);
934 } else if (node.argumentsNode is Prefix) {
935 // Unary operator.
936 return visitDynamicSend(node);
937 } else if ('===' == op.source
938 || '!==' == op.source) {
939 node.visitChildren(this);
940 return types.boolType;
941 } else if ('!=' == op.source) {
942 visitDynamicSend(node);
943 return types.boolType;
944 } else {
945 // Binary operator.
946 return visitDynamicSend(node);
947 }
948 }
949
950 // Because some nodes just visit their children, we may end up
951 // visiting a type annotation, that may contain a send in case of a
952 // prefixed type. Therefore we explicitly visit the type annotation
953 // to avoid confusing the [ResolvedVisitor].
954 visitTypeAnnotation(TypeAnnotation node) {}
955
956 T visitConditional(Conditional node) {
957 List<Send> tests = <Send>[];
958 bool simpleCondition = handleCondition(node.condition, tests);
959 LocalsHandler<T> saved = locals;
960 locals = new LocalsHandler<T>.from(locals, node);
961 updateIsChecks(tests, usePositive: true);
962 T firstType = visit(node.thenExpression);
963 LocalsHandler<T> thenLocals = locals;
964 locals = new LocalsHandler<T>.from(saved, node);
965 if (simpleCondition) updateIsChecks(tests, usePositive: false);
966 T secondType = visit(node.elseExpression);
967 saved.mergeDiamondFlow(thenLocals, locals);
968 locals = saved;
969 T type = types.allocateDiamondPhi(firstType, secondType);
970 return type;
971 }
972
973 T visitVariableDefinitions(VariableDefinitions node) {
974 for (Link<Node> link = node.definitions.nodes;
975 !link.isEmpty;
976 link = link.tail) {
977 Node definition = link.head;
978 if (definition is Identifier) {
979 locals.update(elements[definition], types.nullType, node);
980 } else {
981 assert(definition.asSendSet() != null);
982 visit(definition);
983 }
984 }
985 return null;
986 }
987
988 bool handleCondition(Node node, List<Send> tests) {
989 bool oldConditionIsSimple = conditionIsSimple;
990 bool oldAccumulateIsChecks = accumulateIsChecks;
991 List<Send> oldIsChecks = isChecks;
992 accumulateIsChecks = true;
993 conditionIsSimple = true;
994 isChecks = tests;
995 visit(node);
996 bool simpleCondition = conditionIsSimple;
997 accumulateIsChecks = oldAccumulateIsChecks;
998 isChecks = oldIsChecks;
999 conditionIsSimple = oldConditionIsSimple;
1000 return simpleCondition;
1001 }
1002
1003 T visitIf(If node) {
1004 List<Send> tests = <Send>[];
1005 bool simpleCondition = handleCondition(node.condition, tests);
1006 LocalsHandler<T> saved = locals;
1007 locals = new LocalsHandler<T>.from(locals, node);
1008 updateIsChecks(tests, usePositive: true);
1009 visit(node.thenPart);
1010 LocalsHandler<T> thenLocals = locals;
1011 locals = new LocalsHandler<T>.from(saved, node);
1012 if (simpleCondition) updateIsChecks(tests, usePositive: false);
1013 visit(node.elsePart);
1014 saved.mergeDiamondFlow(thenLocals, locals);
1015 locals = saved;
1016 return null;
1017 }
1018
1019 void setupBreaksAndContinues(JumpTarget element) {
1020 if (element == null) return;
1021 if (element.isContinueTarget) continuesFor[element] = <LocalsHandler>[];
1022 if (element.isBreakTarget) breaksFor[element] = <LocalsHandler>[];
1023 }
1024
1025 void clearBreaksAndContinues(JumpTarget element) {
1026 continuesFor.remove(element);
1027 breaksFor.remove(element);
1028 }
1029
1030 List<LocalsHandler<T>> getBreaks(JumpTarget element) {
1031 List<LocalsHandler<T>> list = <LocalsHandler<T>>[locals];
1032 if (element == null) return list;
1033 if (!element.isBreakTarget) return list;
1034 return list..addAll(breaksFor[element]);
1035 }
1036
1037 List<LocalsHandler<T>> getLoopBackEdges(JumpTarget element) {
1038 List<LocalsHandler<T>> list = <LocalsHandler<T>>[locals];
1039 if (element == null) return list;
1040 if (!element.isContinueTarget) return list;
1041 return list..addAll(continuesFor[element]);
1042 }
1043
1044 T handleLoop(Node node, void logic()) {
1045 loopLevel++;
1046 bool changed = false;
1047 JumpTarget target = elements.getTargetDefinition(node);
1048 LocalsHandler<T> saved = locals;
1049 saved.startLoop(node);
1050 do {
1051 // Setup (and clear in case of multiple iterations of the loop)
1052 // the lists of breaks and continues seen in the loop.
1053 setupBreaksAndContinues(target);
1054 locals = new LocalsHandler<T>.from(saved, node);
1055 logic();
1056 changed = saved.mergeAll(getLoopBackEdges(target));
1057 } while (changed);
1058 loopLevel--;
1059 saved.endLoop(node);
1060 bool keepOwnLocals = node.asDoWhile() == null;
1061 saved.mergeAfterBreaks(
1062 getBreaks(target), keepOwnLocals: keepOwnLocals);
1063 locals = saved;
1064 clearBreaksAndContinues(target);
1065 return null;
1066 }
1067
1068 T visitWhile(While node) {
1069 return handleLoop(node, () {
1070 List<Send> tests = <Send>[];
1071 handleCondition(node.condition, tests);
1072 updateIsChecks(tests, usePositive: true);
1073 visit(node.body);
1074 });
1075 }
1076
1077 T visitDoWhile(DoWhile node) {
1078 return handleLoop(node, () {
1079 visit(node.body);
1080 List<Send> tests = <Send>[];
1081 handleCondition(node.condition, tests);
1082 updateIsChecks(tests, usePositive: true);
1083 });
1084 }
1085
1086 T visitFor(For node) {
1087 visit(node.initializer);
1088 return handleLoop(node, () {
1089 List<Send> tests = <Send>[];
1090 handleCondition(node.condition, tests);
1091 updateIsChecks(tests, usePositive: true);
1092 visit(node.body);
1093 visit(node.update);
1094 });
1095 }
1096
1097 T visitTryStatement(TryStatement node) {
1098 LocalsHandler<T> saved = locals;
1099 locals = new LocalsHandler<T>.from(
1100 locals, node, useOtherTryBlock: false);
1101 visit(node.tryBlock);
1102 saved.mergeDiamondFlow(locals, null);
1103 locals = saved;
1104 for (Node catchBlock in node.catchBlocks) {
1105 saved = locals;
1106 locals = new LocalsHandler<T>.from(locals, catchBlock);
1107 visit(catchBlock);
1108 saved.mergeDiamondFlow(locals, null);
1109 locals = saved;
1110 }
1111 visit(node.finallyBlock);
1112 return null;
1113 }
1114
1115 T visitThrow(Throw node) {
1116 node.visitChildren(this);
1117 locals.seenReturnOrThrow = true;
1118 return types.nonNullEmpty();
1119 }
1120
1121 T visitCatchBlock(CatchBlock node) {
1122 Node exception = node.exception;
1123 if (exception != null) {
1124 DartType type = elements.getType(node.type);
1125 T mask = type == null ||
1126 type.treatAsDynamic ||
1127 type.isTypeVariable
1128 ? types.dynamicType
1129 : types.nonNullSubtype(type.element);
1130 locals.update(elements[exception], mask, node);
1131 }
1132 Node trace = node.trace;
1133 if (trace != null) {
1134 locals.update(elements[trace], types.dynamicType, node);
1135 }
1136 visit(node.block);
1137 return null;
1138 }
1139
1140 T visitParenthesizedExpression(ParenthesizedExpression node) {
1141 return visit(node.expression);
1142 }
1143
1144 T visitBlock(Block node) {
1145 if (node.statements != null) {
1146 for (Node statement in node.statements) {
1147 visit(statement);
1148 if (locals.aborts) break;
1149 }
1150 }
1151 return null;
1152 }
1153
1154 T visitLabeledStatement(LabeledStatement node) {
1155 Statement body = node.statement;
1156 if (body is Loop
1157 || body is SwitchStatement
1158 || Elements.isUnusedLabel(node, elements)) {
1159 // Loops and switches handle their own labels.
1160 visit(body);
1161 } else {
1162 JumpTarget targetElement = elements.getTargetDefinition(body);
1163 setupBreaksAndContinues(targetElement);
1164 visit(body);
1165 locals.mergeAfterBreaks(getBreaks(targetElement));
1166 clearBreaksAndContinues(targetElement);
1167 }
1168 return null;
1169 }
1170
1171 T visitBreakStatement(BreakStatement node) {
1172 JumpTarget target = elements.getTargetOf(node);
1173 locals.seenBreakOrContinue = true;
1174 // Do a deep-copy of the locals, because the code following the
1175 // break will change them.
1176 breaksFor[target].add(new LocalsHandler<T>.deepCopyOf(locals));
1177 return null;
1178 }
1179
1180 T visitContinueStatement(ContinueStatement node) {
1181 JumpTarget target = elements.getTargetOf(node);
1182 locals.seenBreakOrContinue = true;
1183 // Do a deep-copy of the locals, because the code following the
1184 // continue will change them.
1185 continuesFor[target].add(new LocalsHandler<T>.deepCopyOf(locals));
1186 return null;
1187 }
1188
1189 void internalError(String reason, {Node node}) {
1190 compiler.internalError(node, reason);
1191 }
1192
1193 T visitSwitchStatement(SwitchStatement node) {
1194 visit(node.parenthesizedExpression);
1195
1196 setupBreaksAndContinues(elements.getTargetDefinition(node));
1197 if (Elements.switchStatementHasContinue(node, elements)) {
1198 void forEachLabeledCase(void action(JumpTarget target)) {
1199 for (SwitchCase switchCase in node.cases) {
1200 for (Node labelOrCase in switchCase.labelsAndCases) {
1201 if (labelOrCase.asLabel() == null) continue;
1202 LabelDefinition labelElement =
1203 elements.getLabelDefinition(labelOrCase);
1204 if (labelElement != null) {
1205 action(labelElement.target);
1206 }
1207 }
1208 }
1209 }
1210
1211 forEachLabeledCase((JumpTarget target) {
1212 setupBreaksAndContinues(target);
1213 });
1214
1215 // If the switch statement has a continue, we conservatively
1216 // visit all cases and update [locals] until we have reached a
1217 // fixed point.
1218 bool changed;
1219 locals.startLoop(node);
1220 do {
1221 changed = false;
1222 for (Node switchCase in node.cases) {
1223 LocalsHandler<T> saved = locals;
1224 locals = new LocalsHandler<T>.from(locals, switchCase);
1225 visit(switchCase);
1226 changed = saved.mergeAll([locals]) || changed;
1227 locals = saved;
1228 }
1229 } while (changed);
1230 locals.endLoop(node);
1231
1232 forEachLabeledCase((JumpTarget target) {
1233 clearBreaksAndContinues(target);
1234 });
1235 } else {
1236 LocalsHandler<T> saved = locals;
1237 List<LocalsHandler<T>> localsToMerge = <LocalsHandler<T>>[];
1238 bool hasDefaultCase = false;
1239
1240 for (SwitchCase switchCase in node.cases) {
1241 if (switchCase.isDefaultCase) {
1242 hasDefaultCase = true;
1243 }
1244 locals = new LocalsHandler<T>.from(saved, switchCase);
1245 visit(switchCase);
1246 localsToMerge.add(locals);
1247 }
1248 saved.mergeAfterBreaks(localsToMerge, keepOwnLocals: !hasDefaultCase);
1249 locals = saved;
1250 }
1251 clearBreaksAndContinues(elements.getTargetDefinition(node));
1252 return null;
1253 }
1254
1255 T visitCascadeReceiver(CascadeReceiver node) {
1256 var type = visit(node.expression);
1257 cascadeReceiverStack.add(type);
1258 return type;
1259 }
1260
1261 T visitCascade(Cascade node) {
1262 // Ignore the result of the cascade send and return the type of the cascade
1263 // receiver.
1264 visit(node.expression);
1265 return cascadeReceiverStack.removeLast();
1266 }
1267 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698