OLD | NEW |
| (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 } | |
OLD | NEW |