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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/inferrer/simple_types_inferrer.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 simple_types_inferrer;
6
7 import '../closure.dart' show ClosureClassMap, ClosureScope;
8 import '../dart_types.dart'
9 show DartType, InterfaceType, FunctionType, TypeKind;
10 import '../elements/elements.dart';
11 import '../js_backend/js_backend.dart' as js;
12 import '../native/native.dart' as native;
13 import '../tree/tree.dart' as ast;
14 import '../cps_ir/cps_ir_nodes.dart' as cps_ir show Node;
15 import '../util/util.dart' show Link, Spannable, Setlet;
16 import '../types/types.dart'
17 show TypesInferrer, FlatTypeMask, TypeMask, ContainerTypeMask,
18 ElementTypeMask, ValueTypeMask, TypeSystem, MinimalInferrerEngine;
19 import 'inferrer_visitor.dart';
20
21 // BUG(8802): There's a bug in the analyzer that makes the re-export
22 // of Selector from dart2jslib.dart fail. For now, we work around that
23 // by importing universe.dart explicitly and disabling the re-export.
24 import '../dart2jslib.dart' hide Selector, TypedSelector;
25 import '../universe/universe.dart' show Selector, SideEffects, TypedSelector;
26
27 /**
28 * An implementation of [TypeSystem] for [TypeMask].
29 */
30 class TypeMaskSystem implements TypeSystem<TypeMask> {
31 final Compiler compiler;
32 final ClassWorld classWorld;
33 TypeMaskSystem(Compiler compiler)
34 : this.compiler = compiler,
35 this.classWorld = compiler.world;
36
37 TypeMask narrowType(TypeMask type,
38 DartType annotation,
39 {bool isNullable: true}) {
40 if (annotation.treatAsDynamic) return type;
41 if (annotation.element == compiler.objectClass) return type;
42 TypeMask otherType;
43 if (annotation.isTypedef || annotation.isFunctionType) {
44 otherType = functionType;
45 } else if (annotation.isTypeVariable) {
46 // TODO(ngeoffray): Narrow to bound.
47 return type;
48 } else if (annotation.isVoid) {
49 otherType = nullType;
50 } else {
51 assert(annotation.isInterfaceType);
52 otherType = new TypeMask.nonNullSubtype(annotation.element, classWorld);
53 }
54 if (isNullable) otherType = otherType.nullable();
55 if (type == null) return otherType;
56 return type.intersection(otherType, classWorld);
57 }
58
59 TypeMask computeLUB(TypeMask firstType, TypeMask secondType) {
60 if (firstType == null) {
61 return secondType;
62 } else if (secondType == dynamicType || firstType == dynamicType) {
63 return dynamicType;
64 } else if (firstType == secondType) {
65 return firstType;
66 } else {
67 TypeMask union = firstType.union(secondType, classWorld);
68 // TODO(kasperl): If the union isn't nullable it seems wasteful
69 // to use dynamic. Fix that.
70 return union.containsAll(classWorld) ? dynamicType : union;
71 }
72 }
73
74 TypeMask allocateDiamondPhi(TypeMask firstType, TypeMask secondType) {
75 return computeLUB(firstType, secondType);
76 }
77
78 TypeMask get dynamicType => compiler.typesTask.dynamicType;
79 TypeMask get nullType => compiler.typesTask.nullType;
80 TypeMask get intType => compiler.typesTask.intType;
81 TypeMask get uint32Type => compiler.typesTask.uint32Type;
82 TypeMask get uint31Type => compiler.typesTask.uint31Type;
83 TypeMask get positiveIntType => compiler.typesTask.positiveIntType;
84 TypeMask get doubleType => compiler.typesTask.doubleType;
85 TypeMask get numType => compiler.typesTask.numType;
86 TypeMask get boolType => compiler.typesTask.boolType;
87 TypeMask get functionType => compiler.typesTask.functionType;
88 TypeMask get listType => compiler.typesTask.listType;
89 TypeMask get constListType => compiler.typesTask.constListType;
90 TypeMask get fixedListType => compiler.typesTask.fixedListType;
91 TypeMask get growableListType => compiler.typesTask.growableListType;
92 TypeMask get mapType => compiler.typesTask.mapType;
93 TypeMask get constMapType => compiler.typesTask.constMapType;
94 TypeMask get stringType => compiler.typesTask.stringType;
95 TypeMask get typeType => compiler.typesTask.typeType;
96 bool isNull(TypeMask mask) => mask.isEmpty && mask.isNullable;
97
98 TypeMask stringLiteralType(ast.DartString value) => stringType;
99
100 TypeMask nonNullSubtype(ClassElement type)
101 => new TypeMask.nonNullSubtype(type.declaration, classWorld);
102 TypeMask nonNullSubclass(ClassElement type)
103 => new TypeMask.nonNullSubclass(type.declaration, classWorld);
104 TypeMask nonNullExact(ClassElement type)
105 => new TypeMask.nonNullExact(type.declaration, classWorld);
106 TypeMask nonNullEmpty() => new TypeMask.nonNullEmpty();
107
108 TypeMask allocateList(TypeMask type,
109 ast.Node node,
110 Element enclosing,
111 [TypeMask elementType, int length]) {
112 return new ContainerTypeMask(type, node, enclosing, elementType, length);
113 }
114
115 TypeMask allocateMap(TypeMask type, ast.Node node, Element element,
116 [List<TypeMask> keys, List<TypeMask> values]) {
117 return type;
118 }
119
120 TypeMask allocateClosure(ast.Node node, Element element) {
121 return functionType;
122 }
123
124 Selector newTypedSelector(TypeMask receiver, Selector selector) {
125 return new TypedSelector(receiver, selector, compiler.world);
126 }
127
128 TypeMask addPhiInput(Local variable,
129 TypeMask phiType,
130 TypeMask newType) {
131 return computeLUB(phiType, newType);
132 }
133
134 TypeMask allocatePhi(ast.Node node,
135 Local variable,
136 TypeMask inputType) {
137 return inputType;
138 }
139
140 TypeMask allocateLoopPhi(ast.Node node,
141 Local variable,
142 TypeMask inputType) {
143 return inputType;
144 }
145
146 TypeMask simplifyPhi(ast.Node node,
147 Local variable,
148 TypeMask phiType) {
149 return phiType;
150 }
151
152 bool selectorNeedsUpdate(TypeMask type, Selector selector) {
153 return type != selector.mask;
154 }
155
156 TypeMask refineReceiver(Selector selector, TypeMask receiverType) {
157 TypeMask newType = compiler.world.allFunctions.receiverType(selector);
158 return receiverType.intersection(newType, classWorld);
159 }
160
161 TypeMask getConcreteTypeFor(TypeMask mask) => mask;
162 }
163
164 /**
165 * Common super class used by [SimpleTypeInferrerVisitor] to propagate
166 * type information about visited nodes, as well as to request type
167 * information of elements.
168 */
169 abstract class InferrerEngine<T, V extends TypeSystem>
170 implements MinimalInferrerEngine<T> {
171 final Compiler compiler;
172 final ClassWorld classWorld;
173 final V types;
174 final Map<ast.Node, T> concreteTypes = new Map<ast.Node, T>();
175 final Set<Element> generativeConstructorsExposingThis = new Set<Element>();
176
177 InferrerEngine(Compiler compiler, this.types)
178 : this.compiler = compiler,
179 this.classWorld = compiler.world;
180
181 /**
182 * Records the default type of parameter [parameter].
183 */
184 void setDefaultTypeOfParameter(ParameterElement parameter, T type);
185
186 /**
187 * This helper breaks abstractions but is currently required to work around
188 * the wrong modelling of default values of optional parameters of
189 * synthetic constructors.
190 *
191 * TODO(johnniwinther): Remove once default values of synthetic parameters
192 * are fixed.
193 */
194 bool hasAlreadyComputedTypeOfParameterDefault(ParameterElement paramemter);
195
196 /**
197 * Returns the type of [element].
198 */
199 T typeOfElement(Element element);
200
201 /**
202 * Returns the return type of [element].
203 */
204 T returnTypeOfElement(Element element);
205
206 /**
207 * Records that [node] sets final field [element] to be of type [type].
208 *
209 * [nodeHolder] is the element holder of [node].
210 */
211 void recordTypeOfFinalField(ast.Node node,
212 Element nodeHolder,
213 Element field,
214 T type);
215
216 /**
217 * Records that [node] sets non-final field [element] to be of type
218 * [type].
219 */
220 void recordTypeOfNonFinalField(Spannable node, Element field, T type);
221
222 /**
223 * Records that [element] is of type [type].
224 */
225 void recordType(Element element, T type);
226
227 /**
228 * Records that the return type [element] is of type [type].
229 */
230 void recordReturnType(Element element, T type);
231
232 /**
233 * Registers that [caller] calls [callee] at location [node], with
234 * [selector], and [arguments]. Note that [selector] is null for
235 * forwarding constructors.
236 *
237 * [sideEffects] will be updated to incorporate [callee]'s side
238 * effects.
239 *
240 * [inLoop] tells whether the call happens in a loop.
241 */
242 T registerCalledElement(Spannable node,
243 Selector selector,
244 Element caller,
245 Element callee,
246 ArgumentsTypes<T> arguments,
247 SideEffects sideEffects,
248 bool inLoop);
249
250 /**
251 * Registers that [caller] calls [selector] with [receiverType] as
252 * receiver, and [arguments].
253 *
254 * [sideEffects] will be updated to incorporate the potential
255 * callees' side effects.
256 *
257 * [inLoop] tells whether the call happens in a loop.
258 */
259 T registerCalledSelector(ast.Node node,
260 Selector selector,
261 T receiverType,
262 Element caller,
263 ArgumentsTypes<T> arguments,
264 SideEffects sideEffects,
265 bool inLoop);
266
267 /**
268 * Registers that [caller] calls [closure] with [arguments].
269 *
270 * [sideEffects] will be updated to incorporate the potential
271 * callees' side effects.
272 *
273 * [inLoop] tells whether the call happens in a loop.
274 */
275 T registerCalledClosure(ast.Node node,
276 Selector selector,
277 T closure,
278 Element caller,
279 ArgumentsTypes<T> arguments,
280 SideEffects sideEffects,
281 bool inLoop);
282
283 /**
284 * Notifies to the inferrer that [analyzedElement] can have return
285 * type [newType]. [currentType] is the type the [InferrerVisitor]
286 * currently found.
287 *
288 * Returns the new type for [analyzedElement].
289 */
290 T addReturnTypeFor(Element analyzedElement, T currentType, T newType);
291
292 /**
293 * Applies [f] to all elements in the universe that match
294 * [selector]. If [f] returns false, aborts the iteration.
295 */
296 void forEachElementMatching(Selector selector, bool f(Element element)) {
297 Iterable<Element> elements = compiler.world.allFunctions.filter(selector);
298 for (Element e in elements) {
299 if (!f(e.implementation)) return;
300 }
301 }
302
303 /**
304 * Update [sideEffects] with the side effects of [callee] being
305 * called with [selector].
306 */
307 void updateSideEffects(SideEffects sideEffects,
308 Selector selector,
309 Element callee) {
310 if (callee.isField) {
311 if (callee.isInstanceMember) {
312 if (selector.isSetter) {
313 sideEffects.setChangesInstanceProperty();
314 } else if (selector.isGetter) {
315 sideEffects.setDependsOnInstancePropertyStore();
316 } else {
317 sideEffects.setAllSideEffects();
318 sideEffects.setDependsOnSomething();
319 }
320 } else {
321 if (selector.isSetter) {
322 sideEffects.setChangesStaticProperty();
323 } else if (selector.isGetter) {
324 sideEffects.setDependsOnStaticPropertyStore();
325 } else {
326 sideEffects.setAllSideEffects();
327 sideEffects.setDependsOnSomething();
328 }
329 }
330 } else if (callee.isGetter && !selector.isGetter) {
331 sideEffects.setAllSideEffects();
332 sideEffects.setDependsOnSomething();
333 } else {
334 sideEffects.add(compiler.world.getSideEffectsOfElement(callee));
335 }
336 }
337
338 /**
339 * Returns the type for [nativeBehavior]. See documentation on
340 * [native.NativeBehavior].
341 */
342 T typeOfNativeBehavior(native.NativeBehavior nativeBehavior) {
343 if (nativeBehavior == null) return types.dynamicType;
344 List typesReturned = nativeBehavior.typesReturned;
345 if (typesReturned.isEmpty) return types.dynamicType;
346 T returnType;
347 for (var type in typesReturned) {
348 T mappedType;
349 if (type == native.SpecialType.JsObject) {
350 mappedType = types.nonNullExact(compiler.objectClass);
351 } else if (type.element == compiler.stringClass) {
352 mappedType = types.stringType;
353 } else if (type.element == compiler.intClass) {
354 mappedType = types.intType;
355 } else if (type.element == compiler.doubleClass) {
356 mappedType = types.doubleType;
357 } else if (type.element == compiler.numClass) {
358 mappedType = types.numType;
359 } else if (type.element == compiler.boolClass) {
360 mappedType = types.boolType;
361 } else if (type.element == compiler.nullClass) {
362 mappedType = types.nullType;
363 } else if (type.isVoid) {
364 mappedType = types.nullType;
365 } else if (type.isDynamic) {
366 return types.dynamicType;
367 } else {
368 mappedType = types.nonNullSubtype(type.element);
369 }
370 returnType = types.computeLUB(returnType, mappedType);
371 if (returnType == types.dynamicType) {
372 break;
373 }
374 }
375 return returnType;
376 }
377
378 void updateSelectorInTree(
379 AstElement owner, Spannable node, Selector selector) {
380 if (node is cps_ir.Node) {
381 // TODO(lry): update selector for IrInvokeDynamic.
382 throw "updateSelector for IR node $node";
383 }
384 ast.Node astNode = node;
385 TreeElements elements = owner.resolvedAst.elements;
386 if (astNode.asSendSet() != null) {
387 if (selector.isSetter || selector.isIndexSet) {
388 elements.setSelector(node, selector);
389 } else if (selector.isGetter || selector.isIndex) {
390 elements.setGetterSelectorInComplexSendSet(node, selector);
391 } else {
392 assert(selector.isOperator);
393 elements.setOperatorSelectorInComplexSendSet(node, selector);
394 }
395 } else if (astNode.asSend() != null) {
396 elements.setSelector(node, selector);
397 } else {
398 assert(astNode.asForIn() != null);
399 if (selector.asUntyped == compiler.iteratorSelector) {
400 elements.setIteratorSelector(node, selector);
401 } else if (selector.asUntyped == compiler.currentSelector) {
402 elements.setCurrentSelector(node, selector);
403 } else {
404 assert(selector.asUntyped == compiler.moveNextSelector);
405 elements.setMoveNextSelector(node, selector);
406 }
407 }
408 }
409
410 bool isNativeElement(Element element) {
411 if (element.isNative) return true;
412 return element.isClassMember
413 && element.enclosingClass.isNative
414 && element.isField;
415 }
416
417 void analyze(Element element, ArgumentsTypes arguments);
418
419 bool checkIfExposesThis(Element element) {
420 element = element.implementation;
421 return generativeConstructorsExposingThis.contains(element);
422 }
423
424 void recordExposesThis(Element element, bool exposesThis) {
425 element = element.implementation;
426 if (exposesThis) {
427 generativeConstructorsExposingThis.add(element);
428 }
429 }
430 }
431
432 class SimpleTypeInferrerVisitor<T>
433 extends InferrerVisitor<T, InferrerEngine<T, TypeSystem<T>>> {
434 T returnType;
435 bool visitingInitializers = false;
436 bool isConstructorRedirect = false;
437 bool seenSuperConstructorCall = false;
438 SideEffects sideEffects = new SideEffects.empty();
439 final Element outermostElement;
440 final InferrerEngine<T, TypeSystem<T>> inferrer;
441 final Setlet<Element> capturedVariables = new Setlet<Element>();
442
443 SimpleTypeInferrerVisitor.internal(analyzedElement,
444 this.outermostElement,
445 inferrer,
446 compiler,
447 locals)
448 : super(analyzedElement, inferrer, inferrer.types, compiler, locals),
449 this.inferrer = inferrer {
450 assert(outermostElement != null);
451 }
452
453 SimpleTypeInferrerVisitor(Element element,
454 Compiler compiler,
455 InferrerEngine<T, TypeSystem<T>> inferrer,
456 [LocalsHandler<T> handler])
457 : this.internal(element,
458 element.outermostEnclosingMemberOrTopLevel.implementation,
459 inferrer, compiler, handler);
460
461 void analyzeSuperConstructorCall(Element target, ArgumentsTypes arguments) {
462 inferrer.analyze(target, arguments);
463 isThisExposed = isThisExposed || inferrer.checkIfExposesThis(target);
464 }
465
466 T run() {
467 var node = analyzedElement.node;
468 ast.Expression initializer;
469 if (analyzedElement.isField) {
470 VariableElement fieldElement = analyzedElement;
471 initializer = fieldElement.initializer;
472 if (initializer == null) {
473 // Eagerly bailout, because computing the closure data only
474 // works for functions and field assignments.
475 return types.nullType;
476 }
477 }
478 // Update the locals that are boxed in [locals]. These locals will
479 // be handled specially, in that we are computing their LUB at
480 // each update, and reading them yields the type that was found in a
481 // previous analysis of [outermostElement].
482 ClosureClassMap closureData =
483 compiler.closureToClassMapper.computeClosureToClassMapping(
484 analyzedElement, node, elements);
485 closureData.forEachCapturedVariable((variable, field) {
486 locals.setCaptured(variable, field);
487 });
488 closureData.forEachBoxedVariable((variable, field) {
489 locals.setCapturedAndBoxed(variable, field);
490 });
491 if (analyzedElement.isField) {
492 return visit(initializer);
493 }
494
495 FunctionElement function = analyzedElement;
496 FunctionSignature signature = function.functionSignature;
497 signature.forEachOptionalParameter((ParameterElement element) {
498 ast.Expression defaultValue = element.initializer;
499 // If this is a default value from a different context (because
500 // the current function is synthetic, e.g., a constructor from
501 // a mixin application), we have to start a new inferrer visitor
502 // with the correct context.
503 // TODO(johnniwinther): Remove once function signatures are fixed.
504 SimpleTypeInferrerVisitor visitor = this;
505 if (inferrer.hasAlreadyComputedTypeOfParameterDefault(element)) return;
506 if (element.functionDeclaration != analyzedElement) {
507 visitor = new SimpleTypeInferrerVisitor(
508 element.functionDeclaration, compiler, inferrer);
509 }
510 T type =
511 (defaultValue == null) ? types.nullType : visitor.visit(defaultValue);
512 inferrer.setDefaultTypeOfParameter(element, type);
513 });
514
515 if (analyzedElement.isNative) {
516 // Native methods do not have a body, and we currently just say
517 // they return dynamic.
518 return types.dynamicType;
519 }
520
521 if (analyzedElement.isGenerativeConstructor) {
522 isThisExposed = false;
523 signature.forEachParameter((ParameterElement element) {
524 T parameterType = inferrer.typeOfElement(element);
525 if (element.isInitializingFormal) {
526 InitializingFormalElement initializingFormal = element;
527 if (initializingFormal.fieldElement.isFinal) {
528 inferrer.recordTypeOfFinalField(
529 node,
530 analyzedElement,
531 initializingFormal.fieldElement,
532 parameterType);
533 } else {
534 locals.updateField(initializingFormal.fieldElement, parameterType);
535 inferrer.recordTypeOfNonFinalField(
536 initializingFormal.node,
537 initializingFormal.fieldElement,
538 parameterType);
539 }
540 }
541 locals.update(element, parameterType, node);
542 });
543 ClassElement cls = analyzedElement.enclosingClass;
544 if (analyzedElement.isSynthesized) {
545 node = analyzedElement;
546 ConstructorElement constructor = analyzedElement;
547 synthesizeForwardingCall(node, constructor.definingConstructor);
548 } else {
549 visitingInitializers = true;
550 visit(node.initializers);
551 visitingInitializers = false;
552 // For a generative constructor like: `Foo();`, we synthesize
553 // a call to the default super constructor (the one that takes
554 // no argument). Resolution ensures that such a constructor
555 // exists.
556 if (!isConstructorRedirect
557 && !seenSuperConstructorCall
558 && !cls.isObject) {
559 Selector selector =
560 new Selector.callDefaultConstructor(analyzedElement.library);
561 FunctionElement target = cls.superclass.lookupConstructor(selector);
562 analyzeSuperConstructorCall(target, new ArgumentsTypes([], {}));
563 synthesizeForwardingCall(analyzedElement, target);
564 }
565 visit(node.body);
566 inferrer.recordExposesThis(analyzedElement, isThisExposed);
567 }
568 if (!isConstructorRedirect) {
569 // Iterate over all instance fields, and give a null type to
570 // fields that we haven't initialized for sure.
571 cls.forEachInstanceField((_, FieldElement field) {
572 if (field.isFinal) return;
573 T type = locals.fieldScope.readField(field);
574 if (type == null && field.initializer == null) {
575 inferrer.recordTypeOfNonFinalField(node, field, types.nullType);
576 }
577 });
578 }
579 returnType = types.nonNullExact(cls);
580 } else {
581 signature.forEachParameter((element) {
582 locals.update(element, inferrer.typeOfElement(element), node);
583 });
584 visit(node.body);
585 if (returnType == null) {
586 // No return in the body.
587 returnType = locals.seenReturnOrThrow
588 ? types.nonNullEmpty() // Body always throws.
589 : types.nullType;
590 } else if (!locals.seenReturnOrThrow) {
591 // We haven't seen returns on all branches. So the method may
592 // also return null.
593 returnType = inferrer.addReturnTypeFor(
594 analyzedElement, returnType, types.nullType);
595 }
596 }
597
598 compiler.world.registerSideEffects(analyzedElement, sideEffects);
599 assert(breaksFor.isEmpty);
600 assert(continuesFor.isEmpty);
601 return returnType;
602 }
603
604 T visitFunctionExpression(ast.FunctionExpression node) {
605 // We loose track of [this] in closures (see issue 20840). To be on
606 // the safe side, we mark [this] as exposed here. We could do better by
607 // analyzing the closure.
608 // TODO(herhut): Analyze whether closure exposes this.
609 isThisExposed = true;
610 LocalFunctionElement element = elements.getFunctionDefinition(node);
611 // We don't put the closure in the work queue of the
612 // inferrer, because it will share information with its enclosing
613 // method, like for example the types of local variables.
614 LocalsHandler closureLocals = new LocalsHandler<T>.from(
615 locals, node, useOtherTryBlock: false);
616 SimpleTypeInferrerVisitor visitor = new SimpleTypeInferrerVisitor<T>(
617 element, compiler, inferrer, closureLocals);
618 visitor.run();
619 inferrer.recordReturnType(element, visitor.returnType);
620
621 // Record the types of captured non-boxed variables. Types of
622 // these variables may already be there, because of an analysis of
623 // a previous closure.
624 ClosureClassMap nestedClosureData =
625 compiler.closureToClassMapper.getMappingForNestedFunction(node);
626 nestedClosureData.forEachCapturedVariable((variable, field) {
627 if (!nestedClosureData.isVariableBoxed(variable)) {
628 if (variable == nestedClosureData.thisLocal) {
629 inferrer.recordType(field, thisType);
630 }
631 // The type is null for type parameters.
632 if (locals.locals[variable] == null) return;
633 inferrer.recordType(field, locals.locals[variable]);
634 }
635 capturedVariables.add(variable);
636 });
637
638 return inferrer.concreteTypes.putIfAbsent(node, () {
639 return types.allocateClosure(node, element);
640 });
641 }
642
643 T visitFunctionDeclaration(ast.FunctionDeclaration node) {
644 LocalFunctionElement element = elements.getFunctionDefinition(node.function) ;
645 T type = inferrer.concreteTypes.putIfAbsent(node.function, () {
646 return types.allocateClosure(node.function, element);
647 });
648 locals.update(element, type, node);
649 visit(node.function);
650 return type;
651 }
652
653 T visitStringInterpolation(ast.StringInterpolation node) {
654 // Interpolation could have any effects since it could call any toString()
655 // method.
656 // TODO(sra): This could be modelled by a call to toString() but with a
657 // guaranteed String return type. Interpolation of known types would get
658 // specialized effects. This would not currently be effective since the JS
659 // code in the toString methods for intercepted primitive types is assumed
660 // to have all effects. Effect annotations on JS code would be needed to
661 // get the benefit.
662 sideEffects.setAllSideEffects();
663 return super.visitStringInterpolation(node);
664 }
665
666 T visitLiteralList(ast.LiteralList node) {
667 // We only set the type once. We don't need to re-visit the children
668 // when re-analyzing the node.
669 return inferrer.concreteTypes.putIfAbsent(node, () {
670 T elementType;
671 int length = 0;
672 for (ast.Node element in node.elements.nodes) {
673 T type = visit(element);
674 elementType = elementType == null
675 ? types.allocatePhi(null, null, type)
676 : types.addPhiInput(null, elementType, type);
677 length++;
678 }
679 elementType = elementType == null
680 ? types.nonNullEmpty()
681 : types.simplifyPhi(null, null, elementType);
682 T containerType = node.isConst
683 ? types.constListType
684 : types.growableListType;
685 return types.allocateList(
686 containerType,
687 node,
688 outermostElement,
689 elementType,
690 length);
691 });
692 }
693
694 T visitLiteralMap(ast.LiteralMap node) {
695 return inferrer.concreteTypes.putIfAbsent(node, () {
696 ast.NodeList entries = node.entries;
697 List<T> keyTypes = [];
698 List<T> valueTypes = [];
699
700 for (ast.LiteralMapEntry entry in entries) {
701 keyTypes.add(visit(entry.key));
702 valueTypes.add(visit(entry.value));
703 }
704
705 T type = node.isConst ? types.constMapType : types.mapType;
706 return types.allocateMap(type,
707 node,
708 outermostElement,
709 keyTypes,
710 valueTypes);
711 });
712 }
713
714 bool isThisOrSuper(ast.Node node) => node.isThis() || node.isSuper();
715
716 bool isInClassOrSubclass(Element element) {
717 ClassElement cls = outermostElement.enclosingClass.declaration;
718 ClassElement enclosing = element.enclosingClass.declaration;
719 return compiler.world.isSubclassOf(enclosing, cls);
720 }
721
722 void checkIfExposesThis(Selector selector) {
723 if (isThisExposed) return;
724 inferrer.forEachElementMatching(selector, (element) {
725 if (element.isField) {
726 if (!selector.isSetter
727 && isInClassOrSubclass(element)
728 && !element.modifiers.isFinal
729 && locals.fieldScope.readField(element) == null
730 && element.initializer == null) {
731 // If the field is being used before this constructor
732 // actually had a chance to initialize it, say it can be
733 // null.
734 inferrer.recordTypeOfNonFinalField(
735 analyzedElement.node, element,
736 types.nullType);
737 }
738 // Accessing a field does not expose [:this:].
739 return true;
740 }
741 // TODO(ngeoffray): We could do better here if we knew what we
742 // are calling does not expose this.
743 isThisExposed = true;
744 return false;
745 });
746 }
747
748 bool get inInstanceContext {
749 return (outermostElement.isInstanceMember && !outermostElement.isField)
750 || outermostElement.isGenerativeConstructor;
751 }
752
753 bool treatAsInstanceMember(Element element) {
754 return (Elements.isUnresolved(element) && inInstanceContext)
755 || (element != null && element.isInstanceMember);
756 }
757
758 T visitSendSet(ast.SendSet node) {
759 Element element = elements[node];
760 if (!Elements.isUnresolved(element) && element.impliesType) {
761 node.visitChildren(this);
762 return types.dynamicType;
763 }
764
765 Selector getterSelector =
766 elements.getGetterSelectorInComplexSendSet(node);
767 Selector operatorSelector =
768 elements.getOperatorSelectorInComplexSendSet(node);
769 Selector setterSelector = elements.getSelector(node);
770
771 String op = node.assignmentOperator.source;
772 bool isIncrementOrDecrement = op == '++' || op == '--';
773
774 T receiverType;
775 bool isCallOnThis = false;
776 if (node.receiver == null) {
777 if (treatAsInstanceMember(element)) {
778 receiverType = thisType;
779 isCallOnThis = true;
780 }
781 } else {
782 receiverType = visit(node.receiver);
783 isCallOnThis = isThisOrSuper(node.receiver);
784 }
785
786 T rhsType;
787 T indexType;
788
789 if (isIncrementOrDecrement) {
790 rhsType = types.uint31Type;
791 if (node.isIndex) indexType = visit(node.arguments.head);
792 } else if (node.isIndex) {
793 indexType = visit(node.arguments.head);
794 rhsType = visit(node.arguments.tail.head);
795 } else {
796 rhsType = visit(node.arguments.head);
797 }
798
799 if (!visitingInitializers && !isThisExposed) {
800 for (ast.Node node in node.arguments) {
801 if (isThisOrSuper(node)) {
802 isThisExposed = true;
803 break;
804 }
805 }
806 if (!isThisExposed && isCallOnThis) {
807 checkIfExposesThis(
808 types.newTypedSelector(receiverType, setterSelector));
809 if (getterSelector != null) {
810 checkIfExposesThis(
811 types.newTypedSelector(receiverType, getterSelector));
812 }
813 }
814 }
815
816 if (node.isIndex) {
817 if (op == '=') {
818 // [: foo[0] = 42 :]
819 handleDynamicSend(
820 node,
821 setterSelector,
822 receiverType,
823 new ArgumentsTypes<T>([indexType, rhsType], null));
824 return rhsType;
825 } else {
826 // [: foo[0] += 42 :] or [: foo[0]++ :].
827 T getterType = handleDynamicSend(
828 node,
829 getterSelector,
830 receiverType,
831 new ArgumentsTypes<T>([indexType], null));
832 T returnType = handleDynamicSend(
833 node,
834 operatorSelector,
835 getterType,
836 new ArgumentsTypes<T>([rhsType], null));
837 handleDynamicSend(
838 node,
839 setterSelector,
840 receiverType,
841 new ArgumentsTypes<T>([indexType, returnType], null));
842
843 if (node.isPostfix) {
844 return getterType;
845 } else {
846 return returnType;
847 }
848 }
849 } else if (op == '=') {
850 return handlePlainAssignment(
851 node, element, setterSelector, receiverType, rhsType,
852 node.arguments.head);
853 } else {
854 // [: foo++ :] or [: foo += 1 :].
855 ArgumentsTypes operatorArguments = new ArgumentsTypes<T>([rhsType], null);
856 T getterType;
857 T newType;
858 if (Elements.isErroneousElement(element)) {
859 getterType = types.dynamicType;
860 newType = types.dynamicType;
861 } else if (Elements.isStaticOrTopLevelField(element)) {
862 Element getterElement = elements[node.selector];
863 getterType =
864 handleStaticSend(node, getterSelector, getterElement, null);
865 newType = handleDynamicSend(
866 node, operatorSelector, getterType, operatorArguments);
867 handleStaticSend(
868 node, setterSelector, element,
869 new ArgumentsTypes<T>([newType], null));
870 } else if (Elements.isUnresolved(element)
871 || element.isSetter
872 || element.isField) {
873 getterType = handleDynamicSend(
874 node, getterSelector, receiverType, null);
875 newType = handleDynamicSend(
876 node, operatorSelector, getterType, operatorArguments);
877 handleDynamicSend(node, setterSelector, receiverType,
878 new ArgumentsTypes<T>([newType], null));
879 } else if (element.isLocal) {
880 LocalElement local = element;
881 getterType = locals.use(local);
882 newType = handleDynamicSend(
883 node, operatorSelector, getterType, operatorArguments);
884 locals.update(element, newType, node);
885 } else {
886 // Bogus SendSet, for example [: myMethod += 42 :].
887 getterType = types.dynamicType;
888 newType = handleDynamicSend(
889 node, operatorSelector, getterType, operatorArguments);
890 }
891
892 if (node.isPostfix) {
893 return getterType;
894 } else {
895 return newType;
896 }
897 }
898 }
899
900 T handlePlainAssignment(ast.Node node,
901 Element element,
902 Selector setterSelector,
903 T receiverType,
904 T rhsType,
905 ast.Node rhs) {
906 ArgumentsTypes arguments = new ArgumentsTypes<T>([rhsType], null);
907 if (Elements.isErroneousElement(element)) {
908 // Code will always throw.
909 } else if (Elements.isStaticOrTopLevelField(element)) {
910 handleStaticSend(node, setterSelector, element, arguments);
911 } else if (Elements.isUnresolved(element) || element.isSetter) {
912 if (analyzedElement.isGenerativeConstructor
913 && (node.asSendSet() != null)
914 && (node.asSendSet().receiver != null)
915 && node.asSendSet().receiver.isThis()) {
916 Iterable<Element> targets = compiler.world.allFunctions.filter(
917 types.newTypedSelector(thisType, setterSelector));
918 // We just recognized a field initialization of the form:
919 // `this.foo = 42`. If there is only one target, we can update
920 // its type.
921 if (targets.length == 1) {
922 Element single = targets.first;
923 if (single.isField) {
924 locals.updateField(single, rhsType);
925 }
926 }
927 }
928 handleDynamicSend(
929 node, setterSelector, receiverType, arguments);
930 } else if (element.isField) {
931 if (element.isFinal) {
932 inferrer.recordTypeOfFinalField(
933 node, outermostElement, element, rhsType);
934 } else {
935 if (analyzedElement.isGenerativeConstructor) {
936 locals.updateField(element, rhsType);
937 }
938 if (visitingInitializers) {
939 inferrer.recordTypeOfNonFinalField(node, element, rhsType);
940 } else {
941 handleDynamicSend(
942 node, setterSelector, receiverType, arguments);
943 }
944 }
945 } else if (element.isLocal) {
946 locals.update(element, rhsType, node);
947 }
948 return rhsType;
949 }
950
951 T visitSuperSend(ast.Send node) {
952 Element element = elements[node];
953 ArgumentsTypes arguments = node.isPropertyAccess
954 ? null
955 : analyzeArguments(node.arguments);
956 if (visitingInitializers) {
957 seenSuperConstructorCall = true;
958 analyzeSuperConstructorCall(element, arguments);
959 }
960 Selector selector = elements.getSelector(node);
961 // TODO(ngeoffray): We could do better here if we knew what we
962 // are calling does not expose this.
963 isThisExposed = true;
964 if (Elements.isUnresolved(element)
965 || !selector.applies(element, compiler.world)) {
966 // Ensure we create a node, to make explicit the call to the
967 // `noSuchMethod` handler.
968 return handleDynamicSend(node, selector, superType, arguments);
969 } else if (node.isPropertyAccess
970 || element.isFunction
971 || element.isGenerativeConstructor) {
972 return handleStaticSend(node, selector, element, arguments);
973 } else {
974 return inferrer.registerCalledClosure(
975 node, selector, inferrer.typeOfElement(element),
976 outermostElement, arguments, sideEffects, inLoop);
977 }
978 }
979
980 // Try to find the length given to a fixed array constructor call.
981 int findLength(ast.Send node) {
982 ast.Node firstArgument = node.arguments.head;
983 Element element = elements[firstArgument];
984 ast.LiteralInt length = firstArgument.asLiteralInt();
985 if (length != null) {
986 return length.value;
987 } else if (element != null
988 && element.isField
989 && Elements.isStaticOrTopLevelField(element)
990 && compiler.world.fieldNeverChanges(element)) {
991 var constant =
992 compiler.backend.constants.getConstantForVariable(element);
993 if (constant != null && constant.value.isInt) {
994 return constant.value.primitiveValue;
995 }
996 }
997 return null;
998 }
999
1000 T visitStaticSend(ast.Send node) {
1001 Element element = elements[node];
1002 if (elements.isAssert(node)) {
1003 js.JavaScriptBackend backend = compiler.backend;
1004 element = backend.assertMethod;
1005 }
1006 ArgumentsTypes arguments = analyzeArguments(node.arguments);
1007 if (visitingInitializers) {
1008 if (ast.Initializers.isConstructorRedirect(node)) {
1009 isConstructorRedirect = true;
1010 } else if (ast.Initializers.isSuperConstructorCall(node)) {
1011 seenSuperConstructorCall = true;
1012 analyzeSuperConstructorCall(element, arguments);
1013 }
1014 }
1015 // If we are looking at a new expression on a forwarding factory,
1016 // we have to forward the call to the effective target of the
1017 // factory.
1018 if (element.isFactoryConstructor) {
1019 // TODO(herhut): Remove the while loop once effectiveTarget forwards to
1020 // patches.
1021 while (element.isFactoryConstructor) {
1022 ConstructorElement constructor = element;
1023 if (!constructor.isRedirectingFactory) break;
1024 element = constructor.effectiveTarget.implementation;
1025 }
1026 }
1027 if (element.isForeign(compiler.backend)) {
1028 return handleForeignSend(node);
1029 }
1030 Selector selector = elements.getSelector(node);
1031 // In erroneous code the number of arguments in the selector might not
1032 // match the function element.
1033 // TODO(polux): return nonNullEmpty and check it doesn't break anything
1034 if (!selector.applies(element, compiler.world)) return types.dynamicType;
1035
1036 T returnType = handleStaticSend(node, selector, element, arguments);
1037 if (Elements.isGrowableListConstructorCall(element, node, compiler)) {
1038 return inferrer.concreteTypes.putIfAbsent(
1039 node, () => types.allocateList(
1040 types.growableListType, node, outermostElement,
1041 types.nonNullEmpty(), 0));
1042 } else if (Elements.isFixedListConstructorCall(element, node, compiler)
1043 || Elements.isFilledListConstructorCall(element, node, compiler)) {
1044
1045 int length = findLength(node);
1046 T elementType =
1047 Elements.isFixedListConstructorCall(element, node, compiler)
1048 ? types.nullType
1049 : arguments.positional[1];
1050
1051 return inferrer.concreteTypes.putIfAbsent(
1052 node, () => types.allocateList(
1053 types.fixedListType, node, outermostElement,
1054 elementType, length));
1055 } else if (Elements.isConstructorOfTypedArraySubclass(element, compiler)) {
1056 int length = findLength(node);
1057 ConstructorElement constructor = element.implementation;
1058 constructor = constructor.effectiveTarget;
1059 T elementType = inferrer.returnTypeOfElement(
1060 constructor.enclosingClass.lookupMember('[]'));
1061 return inferrer.concreteTypes.putIfAbsent(
1062 node, () => types.allocateList(
1063 types.nonNullExact(constructor.enclosingClass), node,
1064 outermostElement, elementType, length));
1065 } else if (element.isFunction || element.isConstructor) {
1066 return returnType;
1067 } else {
1068 assert(element.isField || element.isGetter);
1069 return inferrer.registerCalledClosure(
1070 node, selector, inferrer.typeOfElement(element),
1071 outermostElement, arguments, sideEffects, inLoop);
1072 }
1073 }
1074
1075 T handleForeignSend(ast.Send node) {
1076 ArgumentsTypes arguments = analyzeArguments(node.arguments);
1077 Selector selector = elements.getSelector(node);
1078 String name = selector.name;
1079 handleStaticSend(node, selector, elements[node], arguments);
1080 if (name == 'JS' || name == 'JS_EMBEDDED_GLOBAL') {
1081 native.NativeBehavior nativeBehavior =
1082 compiler.enqueuer.resolution.nativeEnqueuer.getNativeBehaviorOf(node);
1083 sideEffects.add(nativeBehavior.sideEffects);
1084 return inferrer.typeOfNativeBehavior(nativeBehavior);
1085 } else if (name == 'JS_GET_NAME'
1086 || name == 'JS_NULL_CLASS_NAME'
1087 || name == 'JS_OBJECT_CLASS_NAME'
1088 || name == 'JS_OPERATOR_IS_PREFIX'
1089 || name == 'JS_OPERATOR_AS_PREFIX'
1090 || name == 'JS_STRING_CONCAT') {
1091 return types.stringType;
1092 } else {
1093 sideEffects.setAllSideEffects();
1094 return types.dynamicType;
1095 }
1096 }
1097
1098 ArgumentsTypes analyzeArguments(Link<ast.Node> arguments) {
1099 List<T> positional = [];
1100 Map<String, T> named;
1101 for (var argument in arguments) {
1102 ast.NamedArgument namedArgument = argument.asNamedArgument();
1103 if (namedArgument != null) {
1104 argument = namedArgument.expression;
1105 if (named == null) named = new Map<String, T>();
1106 named[namedArgument.name.source] = argument.accept(this);
1107 } else {
1108 positional.add(argument.accept(this));
1109 }
1110 // TODO(ngeoffray): We could do better here if we knew what we
1111 // are calling does not expose this.
1112 isThisExposed = isThisExposed || argument.isThis();
1113 }
1114 return new ArgumentsTypes<T>(positional, named);
1115 }
1116
1117 T visitGetterSend(ast.Send node) {
1118 Element element = elements[node];
1119 Selector selector = elements.getSelector(node);
1120 if (Elements.isStaticOrTopLevelField(element)) {
1121 return handleStaticSend(node, selector, element, null);
1122 } else if (Elements.isInstanceSend(node, elements)) {
1123 return visitDynamicSend(node);
1124 } else if (Elements.isStaticOrTopLevelFunction(element)) {
1125 return handleStaticSend(node, selector, element, null);
1126 } else if (Elements.isErroneousElement(element)) {
1127 return types.dynamicType;
1128 } else if (element.isLocal) {
1129 LocalElement local = element;
1130 assert(locals.use(local) != null);
1131 return locals.use(local);
1132 } else {
1133 assert(element is PrefixElement);
1134 return null;
1135 }
1136 }
1137
1138 T visitClosureSend(ast.Send node) {
1139 assert(node.receiver == null);
1140 T closure = node.selector.accept(this);
1141 ArgumentsTypes arguments = analyzeArguments(node.arguments);
1142 Element element = elements[node];
1143 Selector selector = elements.getSelector(node);
1144 if (element != null && element.isFunction) {
1145 assert(element.isLocal);
1146 // This only works for function statements. We need a
1147 // more sophisticated type system with function types to support
1148 // more.
1149 return inferrer.registerCalledElement(
1150 node, selector, outermostElement, element, arguments,
1151 sideEffects, inLoop);
1152 } else {
1153 return inferrer.registerCalledClosure(
1154 node, selector, closure, outermostElement, arguments,
1155 sideEffects, inLoop);
1156 }
1157 }
1158
1159 T handleStaticSend(ast.Node node,
1160 Selector selector,
1161 Element element,
1162 ArgumentsTypes arguments) {
1163 assert(!element.isFactoryConstructor ||
1164 !(element as ConstructorElement).isRedirectingFactory);
1165 // Erroneous elements may be unresolved, for example missing getters.
1166 if (Elements.isUnresolved(element)) return types.dynamicType;
1167 // TODO(herhut): should we follow redirecting constructors here? We would
1168 // need to pay attention of the constructor is pointing to an erroneous
1169 // element.
1170 return inferrer.registerCalledElement(
1171 node, selector, outermostElement, element, arguments,
1172 sideEffects, inLoop);
1173 }
1174
1175 T handleDynamicSend(ast.Node node,
1176 Selector selector,
1177 T receiverType,
1178 ArgumentsTypes arguments) {
1179 assert(receiverType != null);
1180 if (types.selectorNeedsUpdate(receiverType, selector)) {
1181 selector = (receiverType == types.dynamicType)
1182 ? selector.asUntyped
1183 : types.newTypedSelector(receiverType, selector);
1184 inferrer.updateSelectorInTree(analyzedElement, node, selector);
1185 }
1186
1187 // If the receiver of the call is a local, we may know more about
1188 // its type by refining it with the potential targets of the
1189 // calls.
1190 if (node.asSend() != null) {
1191 ast.Node receiver = node.asSend().receiver;
1192 if (receiver != null) {
1193 Element element = elements[receiver];
1194 if (Elements.isLocal(element) && !capturedVariables.contains(element)) {
1195 T refinedType = types.refineReceiver(selector, receiverType);
1196 locals.update(element, refinedType, node);
1197 }
1198 }
1199 }
1200
1201 return inferrer.registerCalledSelector(
1202 node, selector, receiverType, outermostElement, arguments,
1203 sideEffects, inLoop);
1204 }
1205
1206 T visitDynamicSend(ast.Send node) {
1207 Element element = elements[node];
1208 T receiverType;
1209 bool isCallOnThis = false;
1210 if (node.receiver == null) {
1211 if (treatAsInstanceMember(element)) {
1212 isCallOnThis = true;
1213 receiverType = thisType;
1214 }
1215 } else {
1216 ast.Node receiver = node.receiver;
1217 isCallOnThis = isThisOrSuper(receiver);
1218 receiverType = visit(receiver);
1219 }
1220
1221 Selector selector = elements.getSelector(node);
1222 if (!isThisExposed && isCallOnThis) {
1223 checkIfExposesThis(types.newTypedSelector(receiverType, selector));
1224 }
1225
1226 ArgumentsTypes arguments = node.isPropertyAccess
1227 ? null
1228 : analyzeArguments(node.arguments);
1229 if (selector.name == '=='
1230 || selector.name == '!=') {
1231 if (types.isNull(receiverType)) {
1232 potentiallyAddNullCheck(node, node.arguments.head);
1233 return types.boolType;
1234 } else if (types.isNull(arguments.positional[0])) {
1235 potentiallyAddNullCheck(node, node.receiver);
1236 return types.boolType;
1237 }
1238 }
1239 return handleDynamicSend(node, selector, receiverType, arguments);
1240 }
1241
1242 void recordReturnType(T type) {
1243 returnType = inferrer.addReturnTypeFor(analyzedElement, returnType, type);
1244 }
1245
1246 T synthesizeForwardingCall(Spannable node, FunctionElement element) {
1247 element = element.implementation;
1248 FunctionElement function = analyzedElement;
1249 FunctionSignature signature = function.functionSignature;
1250 FunctionSignature calleeSignature = element.functionSignature;
1251 if (!calleeSignature.isCompatibleWith(signature)) {
1252 return types.nonNullEmpty();
1253 }
1254
1255 List<T> unnamed = <T>[];
1256 signature.forEachRequiredParameter((ParameterElement element) {
1257 assert(locals.use(element) != null);
1258 unnamed.add(locals.use(element));
1259 });
1260
1261 Map<String, T> named;
1262 if (signature.optionalParametersAreNamed) {
1263 named = new Map<String, T>();
1264 signature.forEachOptionalParameter((ParameterElement element) {
1265 named[element.name] = locals.use(element);
1266 });
1267 } else {
1268 signature.forEachOptionalParameter((ParameterElement element) {
1269 unnamed.add(locals.use(element));
1270 });
1271 }
1272
1273 ArgumentsTypes arguments = new ArgumentsTypes<T>(unnamed, named);
1274 return inferrer.registerCalledElement(node,
1275 null,
1276 outermostElement,
1277 element,
1278 arguments,
1279 sideEffects,
1280 inLoop);
1281 }
1282 T visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {
1283 Element element = elements.getRedirectingTargetConstructor(node);
1284 if (Elements.isErroneousElement(element)) {
1285 recordReturnType(types.dynamicType);
1286 } else {
1287 // We don't create a selector for redirecting factories, and
1288 // the send is just a property access. Therefore we must
1289 // manually create the [ArgumentsTypes] of the call, and
1290 // manually register [analyzedElement] as a caller of [element].
1291 T mask = synthesizeForwardingCall(node.constructorReference, element);
1292 recordReturnType(mask);
1293 }
1294 locals.seenReturnOrThrow = true;
1295 return null;
1296 }
1297
1298 T visitReturn(ast.Return node) {
1299 ast.Node expression = node.expression;
1300 recordReturnType(expression == null
1301 ? types.nullType
1302 : expression.accept(this));
1303 locals.seenReturnOrThrow = true;
1304 return null;
1305 }
1306
1307 T visitForIn(ast.ForIn node) {
1308 T expressionType = visit(node.expression);
1309 Selector iteratorSelector = elements.getIteratorSelector(node);
1310 Selector currentSelector = elements.getCurrentSelector(node);
1311 Selector moveNextSelector = elements.getMoveNextSelector(node);
1312
1313 T iteratorType =
1314 handleDynamicSend(node, iteratorSelector, expressionType, null);
1315 handleDynamicSend(node, moveNextSelector,
1316 iteratorType, new ArgumentsTypes<T>([], null));
1317 T currentType =
1318 handleDynamicSend(node, currentSelector, iteratorType, null);
1319
1320 if (node.expression.isThis()) {
1321 // Any reasonable implementation of an iterator would expose
1322 // this, so we play it safe and assume it will.
1323 isThisExposed = true;
1324 }
1325
1326 ast.Node identifier = node.declaredIdentifier;
1327 Element element = elements.getForInVariable(node);
1328 Selector selector = elements.getSelector(identifier);
1329
1330 T receiverType;
1331 if (element != null && element.isInstanceMember) {
1332 receiverType = thisType;
1333 } else {
1334 receiverType = types.dynamicType;
1335 }
1336
1337 handlePlainAssignment(identifier, element, selector,
1338 receiverType, currentType,
1339 node.expression);
1340 return handleLoop(node, () {
1341 visit(node.body);
1342 });
1343 }
1344 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698