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

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

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

Powered by Google App Engine
This is Rietveld 408576698