| 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 |