Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library type_graph_inferrer; | |
| 6 | |
| 7 import 'dart:collection' show Queue; | |
| 8 | |
| 9 import '../common.dart'; | 5 import '../common.dart'; |
| 10 import '../common/names.dart' show Identifiers; | 6 import '../common/names.dart'; |
| 11 import '../compiler.dart' show Compiler; | 7 import '../compiler.dart'; |
| 12 import '../constants/expressions.dart' show ConstantExpression; | 8 import '../constants/expressions.dart'; |
| 13 import '../constants/values.dart'; | 9 import '../constants/values.dart'; |
| 14 import '../elements/resolution_types.dart' show ResolutionDartType; | 10 import '../core_types.dart'; |
| 15 import '../elements/elements.dart'; | 11 import '../elements/elements.dart'; |
| 16 import '../js_backend/js_backend.dart' show Annotations, JavaScriptBackend; | 12 import '../js_backend/js_backend.dart'; |
| 17 import '../resolution/tree_elements.dart' show TreeElementMapping; | 13 import '../native/behavior.dart' as native; |
| 18 import '../tree/dartstring.dart' show DartString; | 14 import '../resolution/tree_elements.dart'; |
| 19 import '../tree/tree.dart' as ast show Node, LiteralBool, TryStatement; | 15 import '../tree/nodes.dart' as ast; |
| 20 import '../types/constants.dart' show computeTypeMask; | 16 import '../types/constants.dart'; |
| 21 import '../types/masks.dart' | 17 import '../types/types.dart'; |
| 22 show CommonMasks, ContainerTypeMask, MapTypeMask, TypeMask; | 18 import '../universe/call_structure.dart'; |
| 23 import '../types/types.dart' show TypesInferrer; | 19 import '../universe/selector.dart'; |
| 24 import '../universe/call_structure.dart' show CallStructure; | 20 import '../universe/side_effects.dart'; |
| 25 import '../universe/selector.dart' show Selector; | 21 import '../util/util.dart'; |
| 26 import '../universe/side_effects.dart' show SideEffects; | 22 import '../world.dart'; |
| 27 import '../util/util.dart' show Setlet; | |
| 28 import '../world.dart' show ClosedWorld, ClosedWorldRefiner; | |
| 29 import 'closure_tracer.dart'; | 23 import 'closure_tracer.dart'; |
| 30 import 'debug.dart' as debug; | 24 import 'debug.dart' as debug; |
| 31 import 'inferrer_visitor.dart' show ArgumentsTypes, TypeSystem; | 25 import 'inferrer_visitor.dart'; |
| 32 import 'list_tracer.dart'; | 26 import 'list_tracer.dart'; |
| 33 import 'map_tracer.dart'; | 27 import 'map_tracer.dart'; |
| 34 import 'simple_types_inferrer.dart'; | 28 import 'simple_types_inferrer.dart'; |
| 35 import 'type_graph_dump.dart'; | 29 import 'type_graph_dump.dart'; |
| 30 import 'type_graph_inferrer.dart'; | |
| 36 import 'type_graph_nodes.dart'; | 31 import 'type_graph_nodes.dart'; |
| 37 | 32 import 'type_system.dart'; |
| 38 class TypeInformationSystem extends TypeSystem<TypeInformation> { | |
| 39 final ClosedWorld closedWorld; | |
| 40 | |
| 41 /// [ElementTypeInformation]s for elements. | |
| 42 final Map<Element, TypeInformation> typeInformations = | |
| 43 new Map<Element, TypeInformation>(); | |
| 44 | |
| 45 /// [ListTypeInformation] for allocated lists. | |
| 46 final Map<ast.Node, TypeInformation> allocatedLists = | |
| 47 new Map<ast.Node, TypeInformation>(); | |
| 48 | |
| 49 /// [MapTypeInformation] for allocated Maps. | |
| 50 final Map<ast.Node, TypeInformation> allocatedMaps = | |
| 51 new Map<ast.Node, TypeInformation>(); | |
| 52 | |
| 53 /// Closures found during the analysis. | |
| 54 final Set<TypeInformation> allocatedClosures = new Set<TypeInformation>(); | |
| 55 | |
| 56 /// Cache of [ConcreteTypeInformation]. | |
| 57 final Map<TypeMask, TypeInformation> concreteTypes = | |
| 58 new Map<TypeMask, TypeInformation>(); | |
| 59 | |
| 60 /// List of [TypeInformation]s allocated inside method bodies (calls, | |
| 61 /// narrowing, phis, and containers). | |
| 62 final List<TypeInformation> allocatedTypes = <TypeInformation>[]; | |
| 63 | |
| 64 Iterable<TypeInformation> get allTypes => [ | |
| 65 typeInformations.values, | |
| 66 allocatedLists.values, | |
| 67 allocatedMaps.values, | |
| 68 allocatedClosures, | |
| 69 concreteTypes.values, | |
| 70 allocatedTypes | |
| 71 ].expand((x) => x); | |
| 72 | |
| 73 TypeInformationSystem(this.closedWorld) { | |
| 74 nonNullEmptyType = getConcreteTypeFor(commonMasks.emptyType); | |
| 75 } | |
| 76 | |
| 77 CommonMasks get commonMasks => closedWorld.commonMasks; | |
| 78 | |
| 79 /// Used to group [TypeInformation] nodes by the element that triggered their | |
| 80 /// creation. | |
| 81 MemberTypeInformation _currentMember = null; | |
| 82 MemberTypeInformation get currentMember => _currentMember; | |
| 83 | |
| 84 void withMember(MemberElement element, action) { | |
| 85 assert(invariant(element, _currentMember == null, | |
| 86 message: "Already constructing graph for $_currentMember.")); | |
| 87 _currentMember = getInferredTypeOf(element); | |
| 88 action(); | |
| 89 _currentMember = null; | |
| 90 } | |
| 91 | |
| 92 TypeInformation nullTypeCache; | |
| 93 TypeInformation get nullType { | |
| 94 if (nullTypeCache != null) return nullTypeCache; | |
| 95 return nullTypeCache = getConcreteTypeFor(commonMasks.nullType); | |
| 96 } | |
| 97 | |
| 98 TypeInformation intTypeCache; | |
| 99 TypeInformation get intType { | |
| 100 if (intTypeCache != null) return intTypeCache; | |
| 101 return intTypeCache = getConcreteTypeFor(commonMasks.intType); | |
| 102 } | |
| 103 | |
| 104 TypeInformation uint32TypeCache; | |
| 105 TypeInformation get uint32Type { | |
| 106 if (uint32TypeCache != null) return uint32TypeCache; | |
| 107 return uint32TypeCache = getConcreteTypeFor(commonMasks.uint32Type); | |
| 108 } | |
| 109 | |
| 110 TypeInformation uint31TypeCache; | |
| 111 TypeInformation get uint31Type { | |
| 112 if (uint31TypeCache != null) return uint31TypeCache; | |
| 113 return uint31TypeCache = getConcreteTypeFor(commonMasks.uint31Type); | |
| 114 } | |
| 115 | |
| 116 TypeInformation positiveIntTypeCache; | |
| 117 TypeInformation get positiveIntType { | |
| 118 if (positiveIntTypeCache != null) return positiveIntTypeCache; | |
| 119 return positiveIntTypeCache = | |
| 120 getConcreteTypeFor(commonMasks.positiveIntType); | |
| 121 } | |
| 122 | |
| 123 TypeInformation doubleTypeCache; | |
| 124 TypeInformation get doubleType { | |
| 125 if (doubleTypeCache != null) return doubleTypeCache; | |
| 126 return doubleTypeCache = getConcreteTypeFor(commonMasks.doubleType); | |
| 127 } | |
| 128 | |
| 129 TypeInformation numTypeCache; | |
| 130 TypeInformation get numType { | |
| 131 if (numTypeCache != null) return numTypeCache; | |
| 132 return numTypeCache = getConcreteTypeFor(commonMasks.numType); | |
| 133 } | |
| 134 | |
| 135 TypeInformation boolTypeCache; | |
| 136 TypeInformation get boolType { | |
| 137 if (boolTypeCache != null) return boolTypeCache; | |
| 138 return boolTypeCache = getConcreteTypeFor(commonMasks.boolType); | |
| 139 } | |
| 140 | |
| 141 TypeInformation functionTypeCache; | |
| 142 TypeInformation get functionType { | |
| 143 if (functionTypeCache != null) return functionTypeCache; | |
| 144 return functionTypeCache = getConcreteTypeFor(commonMasks.functionType); | |
| 145 } | |
| 146 | |
| 147 TypeInformation listTypeCache; | |
| 148 TypeInformation get listType { | |
| 149 if (listTypeCache != null) return listTypeCache; | |
| 150 return listTypeCache = getConcreteTypeFor(commonMasks.listType); | |
| 151 } | |
| 152 | |
| 153 TypeInformation constListTypeCache; | |
| 154 TypeInformation get constListType { | |
| 155 if (constListTypeCache != null) return constListTypeCache; | |
| 156 return constListTypeCache = getConcreteTypeFor(commonMasks.constListType); | |
| 157 } | |
| 158 | |
| 159 TypeInformation fixedListTypeCache; | |
| 160 TypeInformation get fixedListType { | |
| 161 if (fixedListTypeCache != null) return fixedListTypeCache; | |
| 162 return fixedListTypeCache = getConcreteTypeFor(commonMasks.fixedListType); | |
| 163 } | |
| 164 | |
| 165 TypeInformation growableListTypeCache; | |
| 166 TypeInformation get growableListType { | |
| 167 if (growableListTypeCache != null) return growableListTypeCache; | |
| 168 return growableListTypeCache = | |
| 169 getConcreteTypeFor(commonMasks.growableListType); | |
| 170 } | |
| 171 | |
| 172 TypeInformation mapTypeCache; | |
| 173 TypeInformation get mapType { | |
| 174 if (mapTypeCache != null) return mapTypeCache; | |
| 175 return mapTypeCache = getConcreteTypeFor(commonMasks.mapType); | |
| 176 } | |
| 177 | |
| 178 TypeInformation constMapTypeCache; | |
| 179 TypeInformation get constMapType { | |
| 180 if (constMapTypeCache != null) return constMapTypeCache; | |
| 181 return constMapTypeCache = getConcreteTypeFor(commonMasks.constMapType); | |
| 182 } | |
| 183 | |
| 184 TypeInformation stringTypeCache; | |
| 185 TypeInformation get stringType { | |
| 186 if (stringTypeCache != null) return stringTypeCache; | |
| 187 return stringTypeCache = getConcreteTypeFor(commonMasks.stringType); | |
| 188 } | |
| 189 | |
| 190 TypeInformation typeTypeCache; | |
| 191 TypeInformation get typeType { | |
| 192 if (typeTypeCache != null) return typeTypeCache; | |
| 193 return typeTypeCache = getConcreteTypeFor(commonMasks.typeType); | |
| 194 } | |
| 195 | |
| 196 TypeInformation dynamicTypeCache; | |
| 197 TypeInformation get dynamicType { | |
| 198 if (dynamicTypeCache != null) return dynamicTypeCache; | |
| 199 return dynamicTypeCache = getConcreteTypeFor(commonMasks.dynamicType); | |
| 200 } | |
| 201 | |
| 202 TypeInformation asyncFutureTypeCache; | |
| 203 TypeInformation get asyncFutureType { | |
| 204 if (asyncFutureTypeCache != null) return asyncFutureTypeCache; | |
| 205 return asyncFutureTypeCache = | |
| 206 getConcreteTypeFor(commonMasks.asyncFutureType); | |
| 207 } | |
| 208 | |
| 209 TypeInformation syncStarIterableTypeCache; | |
| 210 TypeInformation get syncStarIterableType { | |
| 211 if (syncStarIterableTypeCache != null) return syncStarIterableTypeCache; | |
| 212 return syncStarIterableTypeCache = | |
| 213 getConcreteTypeFor(commonMasks.syncStarIterableType); | |
| 214 } | |
| 215 | |
| 216 TypeInformation asyncStarStreamTypeCache; | |
| 217 TypeInformation get asyncStarStreamType { | |
| 218 if (asyncStarStreamTypeCache != null) return asyncStarStreamTypeCache; | |
| 219 return asyncStarStreamTypeCache = | |
| 220 getConcreteTypeFor(commonMasks.asyncStarStreamType); | |
| 221 } | |
| 222 | |
| 223 TypeInformation nonNullEmptyType; | |
| 224 | |
| 225 TypeInformation stringLiteralType(DartString value) { | |
| 226 return new StringLiteralTypeInformation(value, commonMasks.stringType); | |
| 227 } | |
| 228 | |
| 229 TypeInformation boolLiteralType(ast.LiteralBool value) { | |
| 230 return new BoolLiteralTypeInformation(value, commonMasks.boolType); | |
| 231 } | |
| 232 | |
| 233 TypeInformation computeLUB( | |
| 234 TypeInformation firstType, TypeInformation secondType) { | |
| 235 if (firstType == null) return secondType; | |
| 236 if (firstType == secondType) return firstType; | |
| 237 if (firstType == nonNullEmptyType) return secondType; | |
| 238 if (secondType == nonNullEmptyType) return firstType; | |
| 239 if (firstType == dynamicType || secondType == dynamicType) { | |
| 240 return dynamicType; | |
| 241 } | |
| 242 return getConcreteTypeFor( | |
| 243 firstType.type.union(secondType.type, closedWorld)); | |
| 244 } | |
| 245 | |
| 246 bool selectorNeedsUpdate(TypeInformation info, TypeMask mask) { | |
| 247 return info.type != mask; | |
| 248 } | |
| 249 | |
| 250 TypeInformation refineReceiver(Selector selector, TypeMask mask, | |
| 251 TypeInformation receiver, bool isConditional) { | |
| 252 if (receiver.type.isExact) return receiver; | |
| 253 TypeMask otherType = closedWorld.allFunctions.receiverType(selector, mask); | |
| 254 // Conditional sends (a?.b) can still narrow the possible types of `a`, | |
| 255 // however, we still need to consider that `a` may be null. | |
| 256 if (isConditional) { | |
| 257 // Note: we don't check that receiver.type.isNullable here because this is | |
| 258 // called during the graph construction. | |
| 259 otherType = otherType.nullable(); | |
| 260 } | |
| 261 // If this is refining to nullable subtype of `Object` just return | |
| 262 // the receiver. We know the narrowing is useless. | |
| 263 if (otherType.isNullable && otherType.containsAll(closedWorld)) { | |
| 264 return receiver; | |
| 265 } | |
| 266 assert(TypeMask.assertIsNormalized(otherType, closedWorld)); | |
| 267 TypeInformation newType = new NarrowTypeInformation(receiver, otherType); | |
| 268 allocatedTypes.add(newType); | |
| 269 return newType; | |
| 270 } | |
| 271 | |
| 272 TypeInformation narrowType( | |
| 273 TypeInformation type, ResolutionDartType annotation, | |
| 274 {bool isNullable: true}) { | |
| 275 if (annotation.treatAsDynamic) return type; | |
| 276 if (annotation.isVoid) return nullType; | |
| 277 if (annotation.element == closedWorld.commonElements.objectClass && | |
| 278 isNullable) { | |
| 279 return type; | |
| 280 } | |
| 281 TypeMask otherType; | |
| 282 if (annotation.isTypedef || annotation.isFunctionType) { | |
| 283 otherType = functionType.type; | |
| 284 } else if (annotation.isTypeVariable) { | |
| 285 // TODO(ngeoffray): Narrow to bound. | |
| 286 return type; | |
| 287 } else { | |
| 288 assert(annotation.isInterfaceType); | |
| 289 otherType = annotation.element == closedWorld.commonElements.objectClass | |
| 290 ? dynamicType.type.nonNullable() | |
| 291 : new TypeMask.nonNullSubtype(annotation.element, closedWorld); | |
| 292 } | |
| 293 if (isNullable) otherType = otherType.nullable(); | |
| 294 if (type.type.isExact) { | |
| 295 return type; | |
| 296 } else { | |
| 297 assert(TypeMask.assertIsNormalized(otherType, closedWorld)); | |
| 298 TypeInformation newType = new NarrowTypeInformation(type, otherType); | |
| 299 allocatedTypes.add(newType); | |
| 300 return newType; | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 TypeInformation narrowNotNull(TypeInformation type) { | |
| 305 if (type.type.isExact && !type.type.isNullable) { | |
| 306 return type; | |
| 307 } | |
| 308 TypeInformation newType = | |
| 309 new NarrowTypeInformation(type, dynamicType.type.nonNullable()); | |
| 310 allocatedTypes.add(newType); | |
| 311 return newType; | |
| 312 } | |
| 313 | |
| 314 ElementTypeInformation getInferredTypeOf(Element element) { | |
| 315 element = element.implementation; | |
| 316 return typeInformations.putIfAbsent(element, () { | |
| 317 return new ElementTypeInformation(element, this); | |
| 318 }); | |
| 319 } | |
| 320 | |
| 321 ConcreteTypeInformation getConcreteTypeFor(TypeMask mask) { | |
| 322 assert(mask != null); | |
| 323 return concreteTypes.putIfAbsent(mask, () { | |
| 324 return new ConcreteTypeInformation(mask); | |
| 325 }); | |
| 326 } | |
| 327 | |
| 328 String getInferredSignatureOf(FunctionElement function) { | |
| 329 ElementTypeInformation info = getInferredTypeOf(function); | |
| 330 FunctionElement impl = function.implementation; | |
| 331 FunctionSignature signature = impl.functionSignature; | |
| 332 var res = ""; | |
| 333 signature.forEachParameter((Element parameter) { | |
| 334 TypeInformation type = getInferredTypeOf(parameter); | |
| 335 res += "${res.isEmpty ? '(' : ', '}${type.type} ${parameter.name}"; | |
| 336 }); | |
| 337 res += ") -> ${info.type}"; | |
| 338 return res; | |
| 339 } | |
| 340 | |
| 341 TypeInformation nonNullSubtype(ClassElement type) { | |
| 342 return getConcreteTypeFor( | |
| 343 new TypeMask.nonNullSubtype(type.declaration, closedWorld)); | |
| 344 } | |
| 345 | |
| 346 TypeInformation nonNullSubclass(ClassElement type) { | |
| 347 return getConcreteTypeFor( | |
| 348 new TypeMask.nonNullSubclass(type.declaration, closedWorld)); | |
| 349 } | |
| 350 | |
| 351 TypeInformation nonNullExact(ClassElement type) { | |
| 352 return getConcreteTypeFor( | |
| 353 new TypeMask.nonNullExact(type.declaration, closedWorld)); | |
| 354 } | |
| 355 | |
| 356 TypeInformation nonNullEmpty() { | |
| 357 return nonNullEmptyType; | |
| 358 } | |
| 359 | |
| 360 bool isNull(TypeInformation type) { | |
| 361 return type == nullType; | |
| 362 } | |
| 363 | |
| 364 TypeInformation allocateList( | |
| 365 TypeInformation type, ast.Node node, Element enclosing, | |
| 366 [TypeInformation elementType, int length]) { | |
| 367 ClassElement typedDataClass = closedWorld.commonElements.typedDataClass; | |
| 368 bool isTypedArray = typedDataClass != null && | |
| 369 closedWorld.isInstantiated(typedDataClass) && | |
| 370 type.type.satisfies(typedDataClass, closedWorld); | |
| 371 bool isConst = (type.type == commonMasks.constListType); | |
| 372 bool isFixed = | |
| 373 (type.type == commonMasks.fixedListType) || isConst || isTypedArray; | |
| 374 bool isElementInferred = isConst || isTypedArray; | |
| 375 | |
| 376 int inferredLength = isFixed ? length : null; | |
| 377 TypeMask elementTypeMask = | |
| 378 isElementInferred ? elementType.type : dynamicType.type; | |
| 379 ContainerTypeMask mask = new ContainerTypeMask( | |
| 380 type.type, node, enclosing, elementTypeMask, inferredLength); | |
| 381 ElementInContainerTypeInformation element = | |
| 382 new ElementInContainerTypeInformation(currentMember, elementType); | |
| 383 element.inferred = isElementInferred; | |
| 384 | |
| 385 allocatedTypes.add(element); | |
| 386 return allocatedLists[node] = | |
| 387 new ListTypeInformation(currentMember, mask, element, length); | |
| 388 } | |
| 389 | |
| 390 TypeInformation allocateClosure(ast.Node node, Element element) { | |
| 391 TypeInformation result = | |
| 392 new ClosureTypeInformation(currentMember, node, element); | |
| 393 allocatedClosures.add(result); | |
| 394 return result; | |
| 395 } | |
| 396 | |
| 397 TypeInformation allocateMap( | |
| 398 ConcreteTypeInformation type, ast.Node node, Element element, | |
| 399 [List<TypeInformation> keyTypes, List<TypeInformation> valueTypes]) { | |
| 400 assert(keyTypes.length == valueTypes.length); | |
| 401 bool isFixed = (type.type == commonMasks.constMapType); | |
| 402 | |
| 403 TypeMask keyType, valueType; | |
| 404 if (isFixed) { | |
| 405 keyType = keyTypes.fold(nonNullEmptyType.type, | |
| 406 (type, info) => type.union(info.type, closedWorld)); | |
| 407 valueType = valueTypes.fold(nonNullEmptyType.type, | |
| 408 (type, info) => type.union(info.type, closedWorld)); | |
| 409 } else { | |
| 410 keyType = valueType = dynamicType.type; | |
| 411 } | |
| 412 MapTypeMask mask = | |
| 413 new MapTypeMask(type.type, node, element, keyType, valueType); | |
| 414 | |
| 415 TypeInformation keyTypeInfo = | |
| 416 new KeyInMapTypeInformation(currentMember, null); | |
| 417 TypeInformation valueTypeInfo = | |
| 418 new ValueInMapTypeInformation(currentMember, null); | |
| 419 allocatedTypes.add(keyTypeInfo); | |
| 420 allocatedTypes.add(valueTypeInfo); | |
| 421 | |
| 422 MapTypeInformation map = | |
| 423 new MapTypeInformation(currentMember, mask, keyTypeInfo, valueTypeInfo); | |
| 424 | |
| 425 for (int i = 0; i < keyTypes.length; ++i) { | |
| 426 TypeInformation newType = | |
| 427 map.addEntryAssignment(keyTypes[i], valueTypes[i], true); | |
| 428 if (newType != null) allocatedTypes.add(newType); | |
| 429 } | |
| 430 | |
| 431 // Shortcut: If we already have a first approximation of the key/value type, | |
| 432 // start propagating it early. | |
| 433 if (isFixed) map.markAsInferred(); | |
| 434 | |
| 435 allocatedMaps[node] = map; | |
| 436 return map; | |
| 437 } | |
| 438 | |
| 439 TypeMask newTypedSelector(TypeInformation info, TypeMask mask) { | |
| 440 // Only type the selector if [info] is concrete, because the other | |
| 441 // kinds of [TypeInformation] have the empty type at this point of | |
| 442 // analysis. | |
| 443 return info.isConcrete ? info.type : mask; | |
| 444 } | |
| 445 | |
| 446 TypeInformation allocateDiamondPhi( | |
| 447 TypeInformation firstInput, TypeInformation secondInput) { | |
| 448 PhiElementTypeInformation result = | |
| 449 new PhiElementTypeInformation(currentMember, null, false, null); | |
| 450 result.addAssignment(firstInput); | |
| 451 result.addAssignment(secondInput); | |
| 452 allocatedTypes.add(result); | |
| 453 return result; | |
| 454 } | |
| 455 | |
| 456 PhiElementTypeInformation _addPhi( | |
| 457 ast.Node node, Local variable, inputType, bool isLoop) { | |
| 458 PhiElementTypeInformation result = | |
| 459 new PhiElementTypeInformation(currentMember, node, isLoop, variable); | |
| 460 allocatedTypes.add(result); | |
| 461 result.addAssignment(inputType); | |
| 462 return result; | |
| 463 } | |
| 464 | |
| 465 PhiElementTypeInformation allocatePhi( | |
| 466 ast.Node node, Local variable, inputType) { | |
| 467 // Check if [inputType] is a phi for a local updated in | |
| 468 // the try/catch block [node]. If it is, no need to allocate a new | |
| 469 // phi. | |
| 470 if (inputType is PhiElementTypeInformation && | |
| 471 inputType.branchNode == node && | |
| 472 inputType.branchNode is ast.TryStatement) { | |
| 473 return inputType; | |
| 474 } | |
| 475 return _addPhi(node, variable, inputType, false); | |
| 476 } | |
| 477 | |
| 478 PhiElementTypeInformation allocateLoopPhi( | |
| 479 ast.Node node, Local variable, inputType) { | |
| 480 return _addPhi(node, variable, inputType, true); | |
| 481 } | |
| 482 | |
| 483 TypeInformation simplifyPhi( | |
| 484 ast.Node node, Local variable, PhiElementTypeInformation phiType) { | |
| 485 assert(phiType.branchNode == node); | |
| 486 if (phiType.assignments.length == 1) return phiType.assignments.first; | |
| 487 return phiType; | |
| 488 } | |
| 489 | |
| 490 PhiElementTypeInformation addPhiInput(Local variable, | |
| 491 PhiElementTypeInformation phiType, TypeInformation newType) { | |
| 492 phiType.addAssignment(newType); | |
| 493 return phiType; | |
| 494 } | |
| 495 | |
| 496 TypeMask computeTypeMask(Iterable<TypeInformation> assignments) { | |
| 497 return joinTypeMasks(assignments.map((e) => e.type)); | |
| 498 } | |
| 499 | |
| 500 TypeMask joinTypeMasks(Iterable<TypeMask> masks) { | |
| 501 var dynamicType = commonMasks.dynamicType; | |
| 502 // Optimization: we are iterating over masks twice, but because `masks` is a | |
| 503 // mapped iterable, we save the intermediate results to avoid computing them | |
| 504 // again. | |
| 505 var list = []; | |
| 506 for (TypeMask mask in masks) { | |
| 507 // Don't do any work on computing unions if we know that after all that | |
| 508 // work the result will be `dynamic`. | |
| 509 // TODO(sigmund): change to `mask == dynamicType` so we can continue to | |
| 510 // track the non-nullable bit. | |
| 511 if (mask.containsAll(closedWorld)) return dynamicType; | |
| 512 list.add(mask); | |
| 513 } | |
| 514 | |
| 515 TypeMask newType = null; | |
| 516 for (TypeMask mask in list) { | |
| 517 newType = newType == null ? mask : newType.union(mask, closedWorld); | |
| 518 // Likewise - stop early if we already reach dynamic. | |
| 519 if (newType.containsAll(closedWorld)) return dynamicType; | |
| 520 } | |
| 521 | |
| 522 return newType ?? const TypeMask.nonNullEmpty(); | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 /** | |
| 527 * A work queue for the inferrer. It filters out nodes that are tagged as | |
| 528 * [TypeInformation.doNotEnqueue], as well as ensures through | |
| 529 * [TypeInformation.inQueue] that a node is in the queue only once at | |
| 530 * a time. | |
| 531 */ | |
| 532 class WorkQueue { | |
| 533 final Queue<TypeInformation> queue = new Queue<TypeInformation>(); | |
| 534 | |
| 535 void add(TypeInformation element) { | |
| 536 if (element.doNotEnqueue) return; | |
| 537 if (element.inQueue) return; | |
| 538 queue.addLast(element); | |
| 539 element.inQueue = true; | |
| 540 } | |
| 541 | |
| 542 void addAll(Iterable<TypeInformation> all) { | |
| 543 all.forEach(add); | |
| 544 } | |
| 545 | |
| 546 TypeInformation remove() { | |
| 547 TypeInformation element = queue.removeFirst(); | |
| 548 element.inQueue = false; | |
| 549 return element; | |
| 550 } | |
| 551 | |
| 552 bool get isEmpty => queue.isEmpty; | |
| 553 | |
| 554 int get length => queue.length; | |
| 555 } | |
| 556 | 33 |
| 557 /** | 34 /** |
| 558 * An inferencing engine that computes a call graph of | 35 * An inferencing engine that computes a call graph of |
| 559 * [TypeInformation] nodes by visiting the AST of the application, and | 36 * [TypeInformation] nodes by visiting the AST of the application, and |
| 560 * then does the inferencing on the graph. | 37 * then does the inferencing on the graph. |
| 561 * | |
| 562 */ | 38 */ |
| 563 class TypeGraphInferrerEngine | 39 class InferrerEngine { |
| 564 extends InferrerEngine<TypeInformation, TypeInformationSystem> { | |
| 565 final Map<Element, TypeInformation> defaultTypeOfParameter = | 40 final Map<Element, TypeInformation> defaultTypeOfParameter = |
| 566 new Map<Element, TypeInformation>(); | 41 new Map<Element, TypeInformation>(); |
| 567 final List<CallSiteTypeInformation> allocatedCalls = | 42 final List<CallSiteTypeInformation> allocatedCalls = |
| 568 <CallSiteTypeInformation>[]; | 43 <CallSiteTypeInformation>[]; |
| 569 final WorkQueue workQueue = new WorkQueue(); | 44 final WorkQueue workQueue = new WorkQueue(); |
| 570 final Element mainElement; | 45 final Element mainElement; |
| 571 final Set<Element> analyzedElements = new Set<Element>(); | 46 final Set<Element> analyzedElements = new Set<Element>(); |
| 572 | 47 |
| 573 /// The maximum number of times we allow a node in the graph to | 48 /// The maximum number of times we allow a node in the graph to |
| 574 /// change types. If a node reaches that limit, we give up | 49 /// change types. If a node reaches that limit, we give up |
| 575 /// inferencing on it and give it the dynamic type. | 50 /// inferencing on it and give it the dynamic type. |
| 576 final int MAX_CHANGE_COUNT = 6; | 51 final int MAX_CHANGE_COUNT = 6; |
| 577 | 52 |
| 578 int overallRefineCount = 0; | 53 int overallRefineCount = 0; |
| 579 int addedInGraph = 0; | 54 int addedInGraph = 0; |
| 580 | 55 |
| 581 TypeGraphInferrerEngine(Compiler compiler, ClosedWorld closedWorld, | 56 final Compiler compiler; |
| 582 ClosedWorldRefiner closedWorldRefiner, this.mainElement) | 57 |
| 583 : super(compiler, closedWorld, closedWorldRefiner, | 58 /// The [ClosedWorld] on which inference reasoning is based. |
| 584 new TypeInformationSystem(closedWorld)); | 59 final ClosedWorld closedWorld; |
| 60 | |
| 61 final ClosedWorldRefiner closedWorldRefiner; | |
| 62 final TypeSystem types; | |
| 63 final Map<ast.Node, TypeInformation> concreteTypes = | |
| 64 new Map<ast.Node, TypeInformation>(); | |
| 65 final Set<Element> generativeConstructorsExposingThis = new Set<Element>(); | |
| 66 | |
| 67 /// Data computed internally within elements, like the type-mask of a send a | |
| 68 /// list allocation, or a for-in loop. | |
| 69 final Map<Element, GlobalTypeInferenceElementData> inTreeData = | |
| 70 new Map<Element, GlobalTypeInferenceElementData>(); | |
| 71 | |
| 72 InferrerEngine(this.compiler, ClosedWorld closedWorld, | |
| 73 this.closedWorldRefiner, this.mainElement) | |
| 74 : this.types = new TypeSystem(closedWorld), | |
| 75 this.closedWorld = closedWorld; | |
| 76 | |
| 77 CommonElements get commonElements => closedWorld.commonElements; | |
| 78 | |
| 79 /** | |
| 80 * Applies [f] to all elements in the universe that match | |
| 81 * [selector] and [mask]. If [f] returns false, aborts the iteration. | |
| 82 */ | |
| 83 void forEachElementMatching( | |
| 84 Selector selector, TypeMask mask, bool f(Element element)) { | |
| 85 Iterable<Element> elements = | |
| 86 closedWorld.allFunctions.filter(selector, mask); | |
| 87 for (Element e in elements) { | |
| 88 if (!f(e.implementation)) return; | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 // TODO(johnniwinther): Make this private again. | |
| 93 GlobalTypeInferenceElementData dataOf(AstElement element) => inTreeData | |
| 94 .putIfAbsent(element, () => new GlobalTypeInferenceElementData()); | |
| 95 | |
| 96 /** | |
| 97 * Update [sideEffects] with the side effects of [callee] being | |
| 98 * called with [selector]. | |
| 99 */ | |
| 100 void updateSideEffects( | |
| 101 SideEffects sideEffects, Selector selector, Element callee) { | |
| 102 if (callee.isField) { | |
| 103 if (callee.isInstanceMember) { | |
| 104 if (selector.isSetter) { | |
| 105 sideEffects.setChangesInstanceProperty(); | |
| 106 } else if (selector.isGetter) { | |
| 107 sideEffects.setDependsOnInstancePropertyStore(); | |
| 108 } else { | |
| 109 sideEffects.setAllSideEffects(); | |
| 110 sideEffects.setDependsOnSomething(); | |
| 111 } | |
| 112 } else { | |
| 113 if (selector.isSetter) { | |
| 114 sideEffects.setChangesStaticProperty(); | |
| 115 } else if (selector.isGetter) { | |
| 116 sideEffects.setDependsOnStaticPropertyStore(); | |
| 117 } else { | |
| 118 sideEffects.setAllSideEffects(); | |
| 119 sideEffects.setDependsOnSomething(); | |
| 120 } | |
| 121 } | |
| 122 } else if (callee.isGetter && !selector.isGetter) { | |
| 123 sideEffects.setAllSideEffects(); | |
| 124 sideEffects.setDependsOnSomething(); | |
| 125 } else { | |
| 126 sideEffects.add(closedWorldRefiner.getCurrentlyKnownSideEffects(callee)); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 /** | |
| 131 * Returns the type for [nativeBehavior]. See documentation on | |
| 132 * [native.NativeBehavior]. | |
| 133 */ | |
| 134 TypeInformation typeOfNativeBehavior(native.NativeBehavior nativeBehavior) { | |
| 135 if (nativeBehavior == null) return types.dynamicType; | |
| 136 List typesReturned = nativeBehavior.typesReturned; | |
| 137 if (typesReturned.isEmpty) return types.dynamicType; | |
| 138 TypeInformation returnType; | |
| 139 for (var type in typesReturned) { | |
| 140 TypeInformation mappedType; | |
| 141 if (type == native.SpecialType.JsObject) { | |
| 142 mappedType = types.nonNullExact(commonElements.objectClass); | |
| 143 } else if (type == commonElements.stringType) { | |
| 144 mappedType = types.stringType; | |
| 145 } else if (type == commonElements.intType) { | |
| 146 mappedType = types.intType; | |
| 147 } else if (type == commonElements.numType || | |
| 148 type == commonElements.doubleType) { | |
| 149 // Note: the backend double class is specifically for non-integer | |
| 150 // doubles, and a native behavior returning 'double' does not guarantee | |
| 151 // a non-integer return type, so we return the number type for those. | |
| 152 mappedType = types.numType; | |
| 153 } else if (type == commonElements.boolType) { | |
| 154 mappedType = types.boolType; | |
| 155 } else if (type == commonElements.nullType) { | |
| 156 mappedType = types.nullType; | |
| 157 } else if (type.isVoid) { | |
| 158 mappedType = types.nullType; | |
| 159 } else if (type.isDynamic) { | |
| 160 return types.dynamicType; | |
| 161 } else { | |
| 162 mappedType = types.nonNullSubtype(type.element); | |
| 163 } | |
| 164 returnType = types.computeLUB(returnType, mappedType); | |
| 165 if (returnType == types.dynamicType) { | |
| 166 break; | |
| 167 } | |
| 168 } | |
| 169 return returnType; | |
| 170 } | |
| 171 | |
| 172 // TODO(johnniwinther): Pass the [ResolvedAst] instead of [owner]. | |
| 173 void updateSelectorInTree( | |
| 174 AstElement owner, Spannable node, Selector selector, TypeMask mask) { | |
| 175 ast.Node astNode = node; | |
| 176 GlobalTypeInferenceElementData data = dataOf(owner); | |
| 177 if (astNode.asSendSet() != null) { | |
| 178 if (selector.isSetter || selector.isIndexSet) { | |
| 179 data.setTypeMask(node, mask); | |
| 180 } else if (selector.isGetter || selector.isIndex) { | |
| 181 data.setGetterTypeMaskInComplexSendSet(node, mask); | |
| 182 } else { | |
| 183 assert(selector.isOperator); | |
| 184 data.setOperatorTypeMaskInComplexSendSet(node, mask); | |
| 185 } | |
| 186 } else if (astNode.asSend() != null) { | |
| 187 data.setTypeMask(node, mask); | |
| 188 } else { | |
| 189 assert(astNode.asForIn() != null); | |
| 190 if (selector == Selectors.iterator) { | |
| 191 data.setIteratorTypeMask(node, mask); | |
| 192 } else if (selector == Selectors.current) { | |
| 193 data.setCurrentTypeMask(node, mask); | |
| 194 } else { | |
| 195 assert(selector == Selectors.moveNext); | |
| 196 data.setMoveNextTypeMask(node, mask); | |
| 197 } | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 bool isNativeElement(Element element) { | |
| 202 return compiler.backend.isNative(element); | |
| 203 } | |
| 204 | |
| 205 bool checkIfExposesThis(Element element) { | |
| 206 element = element.implementation; | |
| 207 return generativeConstructorsExposingThis.contains(element); | |
| 208 } | |
| 209 | |
| 210 void recordExposesThis(Element element, bool exposesThis) { | |
| 211 element = element.implementation; | |
| 212 if (exposesThis) { | |
| 213 generativeConstructorsExposingThis.add(element); | |
| 214 } | |
| 215 } | |
| 585 | 216 |
| 586 JavaScriptBackend get backend => compiler.backend; | 217 JavaScriptBackend get backend => compiler.backend; |
| 587 Annotations get annotations => backend.annotations; | 218 Annotations get annotations => backend.annotations; |
| 588 DiagnosticReporter get reporter => compiler.reporter; | 219 DiagnosticReporter get reporter => compiler.reporter; |
| 589 CommonMasks get commonMasks => closedWorld.commonMasks; | 220 CommonMasks get commonMasks => closedWorld.commonMasks; |
| 590 | 221 |
| 591 /** | 222 /** |
| 592 * A set of selector names that [List] implements, that we know return | 223 * A set of selector names that [List] implements, that we know return |
| 593 * their element type. | 224 * their element type. |
| 594 */ | 225 */ |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 720 var info = types.getInferredTypeOf(parameter); | 351 var info = types.getInferredTypeOf(parameter); |
| 721 info.maybeResume(); | 352 info.maybeResume(); |
| 722 workQueue.add(info); | 353 workQueue.add(info); |
| 723 }); | 354 }); |
| 724 if (tracer.tracedType.mightBePassedToFunctionApply) { | 355 if (tracer.tracedType.mightBePassedToFunctionApply) { |
| 725 closedWorldRefiner.registerMightBePassedToApply(e); | 356 closedWorldRefiner.registerMightBePassedToApply(e); |
| 726 } | 357 } |
| 727 if (debug.VERBOSE) { | 358 if (debug.VERBOSE) { |
| 728 print("traced closure $e as " | 359 print("traced closure $e as " |
| 729 "${closedWorldRefiner | 360 "${closedWorldRefiner |
| 730 .getCurrentlyKnownMightBePassedToApply(e)}"); | 361 .getCurrentlyKnownMightBePassedToApply(e)}"); |
| 731 } | 362 } |
| 732 }); | 363 }); |
| 733 } | 364 } |
| 734 | 365 |
| 735 if (info is ClosureTypeInformation) { | 366 if (info is ClosureTypeInformation) { |
| 736 Iterable<FunctionElement> elements = [info.element]; | 367 Iterable<FunctionElement> elements = [info.element]; |
| 737 trace(elements, new ClosureTracerVisitor(elements, info, this)); | 368 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 738 } else if (info is CallSiteTypeInformation) { | 369 } else if (info is CallSiteTypeInformation) { |
| 739 if (info is StaticCallSiteTypeInformation && | 370 if (info is StaticCallSiteTypeInformation && |
| 740 info.selector != null && | 371 info.selector != null && |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1079 * should be present and a default type for each parameter should | 710 * should be present and a default type for each parameter should |
| 1080 * exist. | 711 * exist. |
| 1081 */ | 712 */ |
| 1082 TypeInformation getDefaultTypeOfParameter(Element parameter) { | 713 TypeInformation getDefaultTypeOfParameter(Element parameter) { |
| 1083 return defaultTypeOfParameter.putIfAbsent(parameter, () { | 714 return defaultTypeOfParameter.putIfAbsent(parameter, () { |
| 1084 return new PlaceholderTypeInformation(types.currentMember); | 715 return new PlaceholderTypeInformation(types.currentMember); |
| 1085 }); | 716 }); |
| 1086 } | 717 } |
| 1087 | 718 |
| 1088 /** | 719 /** |
| 1089 * Helper to inspect the [TypeGraphInferrer]'s state. To be removed by | 720 * This helper breaks abstractions but is currently required to work around |
| 1090 * TODO(johnniwinther) once synthetic parameters get their own default | 721 * the wrong modelling of default values of optional parameters of |
|
Siggi Cherem (dart-lang)
2017/01/06 18:59:56
modelling => modeling
Johnni Winther
2017/01/09 09:01:49
Done.
| |
| 1091 * values. | 722 * synthetic constructors. |
| 723 * | |
| 724 * TODO(johnniwinther): Remove once default values of synthetic parameters | |
| 725 * are fixed. | |
| 1092 */ | 726 */ |
| 1093 bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) { | 727 bool hasAlreadyComputedTypeOfParameterDefault(Element parameter) { |
| 1094 TypeInformation seen = defaultTypeOfParameter[parameter]; | 728 TypeInformation seen = defaultTypeOfParameter[parameter]; |
| 1095 return (seen != null && seen is! PlaceholderTypeInformation); | 729 return (seen != null && seen is! PlaceholderTypeInformation); |
| 1096 } | 730 } |
| 1097 | 731 |
| 732 /** | |
| 733 * Returns the type of [element]. | |
| 734 */ | |
| 1098 TypeInformation typeOfElement(Element element) { | 735 TypeInformation typeOfElement(Element element) { |
| 1099 if (element is FunctionElement) return types.functionType; | 736 if (element is FunctionElement) return types.functionType; |
| 1100 return types.getInferredTypeOf(element); | 737 return types.getInferredTypeOf(element); |
| 1101 } | 738 } |
| 1102 | 739 |
| 740 /** | |
| 741 * Returns the return type of [element]. | |
| 742 */ | |
| 1103 TypeInformation returnTypeOfElement(Element element) { | 743 TypeInformation returnTypeOfElement(Element element) { |
| 1104 if (element is! FunctionElement) return types.dynamicType; | 744 if (element is! FunctionElement) return types.dynamicType; |
| 1105 return types.getInferredTypeOf(element); | 745 return types.getInferredTypeOf(element); |
| 1106 } | 746 } |
| 1107 | 747 |
| 748 /** | |
| 749 * Records that [node] sets final field [element] to be of type [type]. | |
| 750 * | |
| 751 * [nodeHolder] is the element holder of [node]. | |
| 752 */ | |
| 1108 void recordTypeOfFinalField( | 753 void recordTypeOfFinalField( |
| 1109 Spannable node, Element analyzed, Element element, TypeInformation type) { | 754 Spannable node, Element analyzed, Element element, TypeInformation type) { |
| 1110 types.getInferredTypeOf(element).addAssignment(type); | 755 types.getInferredTypeOf(element).addAssignment(type); |
| 1111 } | 756 } |
| 1112 | 757 |
| 758 /** | |
| 759 * Records that [node] sets non-final field [element] to be of type | |
| 760 * [type]. | |
| 761 */ | |
| 1113 void recordTypeOfNonFinalField( | 762 void recordTypeOfNonFinalField( |
| 1114 Spannable node, Element element, TypeInformation type) { | 763 Spannable node, Element element, TypeInformation type) { |
| 1115 types.getInferredTypeOf(element).addAssignment(type); | 764 types.getInferredTypeOf(element).addAssignment(type); |
| 1116 } | 765 } |
| 1117 | 766 |
| 767 /** | |
| 768 * Records that [element] is of type [type]. | |
| 769 */ | |
| 1118 void recordType(Element element, TypeInformation type) { | 770 void recordType(Element element, TypeInformation type) { |
| 1119 types.getInferredTypeOf(element).addAssignment(type); | 771 types.getInferredTypeOf(element).addAssignment(type); |
| 1120 } | 772 } |
| 1121 | 773 |
| 774 /** | |
| 775 * Records that the return type [element] is of type [type]. | |
| 776 */ | |
| 1122 void recordReturnType(Element element, TypeInformation type) { | 777 void recordReturnType(Element element, TypeInformation type) { |
| 1123 TypeInformation info = types.getInferredTypeOf(element); | 778 TypeInformation info = types.getInferredTypeOf(element); |
| 1124 if (element.name == '==') { | 779 if (element.name == '==') { |
| 1125 // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'. | 780 // Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'. |
| 1126 info.addAssignment(types.boolType); | 781 info.addAssignment(types.boolType); |
| 1127 } | 782 } |
| 1128 // TODO(ngeoffray): Clean up. We do these checks because | 783 // TODO(ngeoffray): Clean up. We do these checks because |
| 1129 // [SimpleTypesInferrer] deals with two different inferrers. | 784 // [SimpleTypesInferrer] deals with two different inferrers. |
| 1130 if (type == null) return; | 785 if (type == null) return; |
| 1131 if (info.assignments.isEmpty) info.addAssignment(type); | 786 if (info.assignments.isEmpty) info.addAssignment(type); |
| 1132 } | 787 } |
| 1133 | 788 |
| 789 /** | |
| 790 * Notifies to the inferrer that [analyzedElement] can have return | |
| 791 * type [newType]. [currentType] is the type the [InferrerVisitor] | |
| 792 * currently found. | |
| 793 * | |
| 794 * Returns the new type for [analyzedElement]. | |
| 795 */ | |
| 1134 TypeInformation addReturnTypeFor( | 796 TypeInformation addReturnTypeFor( |
| 1135 Element element, TypeInformation unused, TypeInformation newType) { | 797 Element element, TypeInformation unused, TypeInformation newType) { |
| 1136 TypeInformation type = types.getInferredTypeOf(element); | 798 TypeInformation type = types.getInferredTypeOf(element); |
| 1137 // TODO(ngeoffray): Clean up. We do this check because | 799 // TODO(ngeoffray): Clean up. We do this check because |
| 1138 // [SimpleTypesInferrer] deals with two different inferrers. | 800 // [SimpleTypesInferrer] deals with two different inferrers. |
| 1139 if (element.isGenerativeConstructor) return type; | 801 if (element.isGenerativeConstructor) return type; |
| 1140 type.addAssignment(newType); | 802 type.addAssignment(newType); |
| 1141 return type; | 803 return type; |
| 1142 } | 804 } |
| 1143 | 805 |
| 806 /** | |
| 807 * Registers that [caller] calls [callee] at location [node], with | |
| 808 * [selector], and [arguments]. Note that [selector] is null for | |
| 809 * forwarding constructors. | |
| 810 * | |
| 811 * [sideEffects] will be updated to incorporate [callee]'s side | |
| 812 * effects. | |
| 813 * | |
| 814 * [inLoop] tells whether the call happens in a loop. | |
| 815 */ | |
| 1144 TypeInformation registerCalledElement( | 816 TypeInformation registerCalledElement( |
| 1145 Spannable node, | 817 Spannable node, |
| 1146 Selector selector, | 818 Selector selector, |
| 1147 TypeMask mask, | 819 TypeMask mask, |
| 1148 Element caller, | 820 Element caller, |
| 1149 Element callee, | 821 Element callee, |
| 1150 ArgumentsTypes arguments, | 822 ArgumentsTypes arguments, |
| 1151 SideEffects sideEffects, | 823 SideEffects sideEffects, |
| 1152 bool inLoop) { | 824 bool inLoop) { |
| 1153 CallSiteTypeInformation info = new StaticCallSiteTypeInformation( | 825 CallSiteTypeInformation info = new StaticCallSiteTypeInformation( |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1166 if (cls.callType != null) { | 838 if (cls.callType != null) { |
| 1167 types.allocatedClosures.add(info); | 839 types.allocatedClosures.add(info); |
| 1168 } | 840 } |
| 1169 } | 841 } |
| 1170 info.addToGraph(this); | 842 info.addToGraph(this); |
| 1171 allocatedCalls.add(info); | 843 allocatedCalls.add(info); |
| 1172 updateSideEffects(sideEffects, selector, callee); | 844 updateSideEffects(sideEffects, selector, callee); |
| 1173 return info; | 845 return info; |
| 1174 } | 846 } |
| 1175 | 847 |
| 848 /** | |
| 849 * Registers that [caller] calls [selector] with [receiverType] as | |
| 850 * receiver, and [arguments]. | |
| 851 * | |
| 852 * [sideEffects] will be updated to incorporate the potential | |
| 853 * callees' side effects. | |
| 854 * | |
| 855 * [inLoop] tells whether the call happens in a loop. | |
| 856 */ | |
| 1176 TypeInformation registerCalledSelector( | 857 TypeInformation registerCalledSelector( |
| 1177 ast.Node node, | 858 ast.Node node, |
| 1178 Selector selector, | 859 Selector selector, |
| 1179 TypeMask mask, | 860 TypeMask mask, |
| 1180 TypeInformation receiverType, | 861 TypeInformation receiverType, |
| 1181 Element caller, | 862 Element caller, |
| 1182 ArgumentsTypes arguments, | 863 ArgumentsTypes arguments, |
| 1183 SideEffects sideEffects, | 864 SideEffects sideEffects, |
| 1184 bool inLoop) { | 865 bool inLoop) { |
| 1185 if (selector.isClosureCall) { | 866 if (selector.isClosureCall) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1199 mask, | 880 mask, |
| 1200 receiverType, | 881 receiverType, |
| 1201 arguments, | 882 arguments, |
| 1202 inLoop); | 883 inLoop); |
| 1203 | 884 |
| 1204 info.addToGraph(this); | 885 info.addToGraph(this); |
| 1205 allocatedCalls.add(info); | 886 allocatedCalls.add(info); |
| 1206 return info; | 887 return info; |
| 1207 } | 888 } |
| 1208 | 889 |
| 890 /** | |
| 891 * Registers a call to await with an expression of type [argumentType] as | |
| 892 * argument. | |
| 893 */ | |
| 1209 TypeInformation registerAwait(ast.Node node, TypeInformation argument) { | 894 TypeInformation registerAwait(ast.Node node, TypeInformation argument) { |
| 1210 AwaitTypeInformation info = | 895 AwaitTypeInformation info = |
| 1211 new AwaitTypeInformation(types.currentMember, node); | 896 new AwaitTypeInformation(types.currentMember, node); |
| 1212 info.addAssignment(argument); | 897 info.addAssignment(argument); |
| 1213 types.allocatedTypes.add(info); | 898 types.allocatedTypes.add(info); |
| 1214 return info; | 899 return info; |
| 1215 } | 900 } |
| 1216 | 901 |
| 902 /** | |
| 903 * Registers that [caller] calls [closure] with [arguments]. | |
| 904 * | |
| 905 * [sideEffects] will be updated to incorporate the potential | |
| 906 * callees' side effects. | |
| 907 * | |
| 908 * [inLoop] tells whether the call happens in a loop. | |
| 909 */ | |
| 1217 TypeInformation registerCalledClosure( | 910 TypeInformation registerCalledClosure( |
| 1218 ast.Node node, | 911 ast.Node node, |
| 1219 Selector selector, | 912 Selector selector, |
| 1220 TypeMask mask, | 913 TypeMask mask, |
| 1221 TypeInformation closure, | 914 TypeInformation closure, |
| 1222 Element caller, | 915 Element caller, |
| 1223 ArgumentsTypes arguments, | 916 ArgumentsTypes arguments, |
| 1224 SideEffects sideEffects, | 917 SideEffects sideEffects, |
| 1225 bool inLoop) { | 918 bool inLoop) { |
| 1226 sideEffects.setDependsOnSomething(); | 919 sideEffects.setDependsOnSomething(); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1339 return returnTypeOfElement(element); | 1032 return returnTypeOfElement(element); |
| 1340 } | 1033 } |
| 1341 } else if (element.isGetter || element.isField) { | 1034 } else if (element.isGetter || element.isField) { |
| 1342 assert(selector.isCall || selector.isSetter); | 1035 assert(selector.isCall || selector.isSetter); |
| 1343 return types.dynamicType; | 1036 return types.dynamicType; |
| 1344 } else { | 1037 } else { |
| 1345 return returnTypeOfElement(element); | 1038 return returnTypeOfElement(element); |
| 1346 } | 1039 } |
| 1347 } | 1040 } |
| 1348 | 1041 |
| 1042 /** | |
| 1043 * Records that the captured variable [local] is read. | |
| 1044 */ | |
| 1349 void recordCapturedLocalRead(Local local) {} | 1045 void recordCapturedLocalRead(Local local) {} |
| 1350 | 1046 |
| 1047 /** | |
| 1048 * Records that the variable [local] is being updated. | |
| 1049 */ | |
| 1351 void recordLocalUpdate(Local local, TypeInformation type) {} | 1050 void recordLocalUpdate(Local local, TypeInformation type) {} |
| 1352 } | 1051 } |
| 1353 | |
| 1354 class TypeGraphInferrer implements TypesInferrer { | |
| 1355 TypeGraphInferrerEngine inferrer; | |
| 1356 final Compiler compiler; | |
| 1357 final ClosedWorld closedWorld; | |
| 1358 final ClosedWorldRefiner closedWorldRefiner; | |
| 1359 | |
| 1360 TypeGraphInferrer(this.compiler, this.closedWorld, this.closedWorldRefiner); | |
| 1361 | |
| 1362 String get name => 'Graph inferrer'; | |
| 1363 | |
| 1364 CommonMasks get commonMasks => closedWorld.commonMasks; | |
| 1365 | |
| 1366 TypeMask get _dynamicType => commonMasks.dynamicType; | |
| 1367 | |
| 1368 void analyzeMain(Element main) { | |
| 1369 inferrer = new TypeGraphInferrerEngine( | |
| 1370 compiler, closedWorld, closedWorldRefiner, main); | |
| 1371 inferrer.runOverAllElements(); | |
| 1372 } | |
| 1373 | |
| 1374 TypeMask getReturnTypeOfElement(Element element) { | |
| 1375 if (compiler.disableTypeInference) return _dynamicType; | |
| 1376 // Currently, closure calls return dynamic. | |
| 1377 if (element is! FunctionElement) return _dynamicType; | |
| 1378 return inferrer.types.getInferredTypeOf(element).type; | |
| 1379 } | |
| 1380 | |
| 1381 TypeMask getTypeOfElement(Element element) { | |
| 1382 if (compiler.disableTypeInference) return _dynamicType; | |
| 1383 // The inferrer stores the return type for a function, so we have to | |
| 1384 // be careful to not return it here. | |
| 1385 if (element is FunctionElement) return commonMasks.functionType; | |
| 1386 return inferrer.types.getInferredTypeOf(element).type; | |
| 1387 } | |
| 1388 | |
| 1389 TypeMask getTypeForNewList(Element owner, ast.Node node) { | |
| 1390 if (compiler.disableTypeInference) return _dynamicType; | |
| 1391 return inferrer.types.allocatedLists[node].type; | |
| 1392 } | |
| 1393 | |
| 1394 bool isFixedArrayCheckedForGrowable(ast.Node node) { | |
| 1395 if (compiler.disableTypeInference) return true; | |
| 1396 ListTypeInformation info = inferrer.types.allocatedLists[node]; | |
| 1397 return info.checksGrowable; | |
| 1398 } | |
| 1399 | |
| 1400 TypeMask getTypeOfSelector(Selector selector, TypeMask mask) { | |
| 1401 if (compiler.disableTypeInference) return _dynamicType; | |
| 1402 // Bailout for closure calls. We're not tracking types of | |
| 1403 // closures. | |
| 1404 if (selector.isClosureCall) return _dynamicType; | |
| 1405 if (selector.isSetter || selector.isIndexSet) { | |
| 1406 return _dynamicType; | |
| 1407 } | |
| 1408 if (inferrer.returnsListElementType(selector, mask)) { | |
| 1409 ContainerTypeMask containerTypeMask = mask; | |
| 1410 TypeMask elementType = containerTypeMask.elementType; | |
| 1411 return elementType == null ? _dynamicType : elementType; | |
| 1412 } | |
| 1413 if (inferrer.returnsMapValueType(selector, mask)) { | |
| 1414 MapTypeMask mapTypeMask = mask; | |
| 1415 TypeMask valueType = mapTypeMask.valueType; | |
| 1416 return valueType == null ? _dynamicType : valueType; | |
| 1417 } | |
| 1418 | |
| 1419 TypeMask result = const TypeMask.nonNullEmpty(); | |
| 1420 Iterable<Element> elements = | |
| 1421 inferrer.closedWorld.allFunctions.filter(selector, mask); | |
| 1422 for (Element element in elements) { | |
| 1423 TypeMask type = | |
| 1424 inferrer.typeOfElementWithSelector(element, selector).type; | |
| 1425 result = result.union(type, inferrer.closedWorld); | |
| 1426 } | |
| 1427 return result; | |
| 1428 } | |
| 1429 | |
| 1430 Iterable<Element> getCallersOf(Element element) { | |
| 1431 if (compiler.disableTypeInference) { | |
| 1432 throw new UnsupportedError( | |
| 1433 "Cannot query the type inferrer when type inference is disabled."); | |
| 1434 } | |
| 1435 return inferrer.getCallersOf(element); | |
| 1436 } | |
| 1437 | |
| 1438 bool isCalledOnce(Element element) { | |
| 1439 if (compiler.disableTypeInference) return false; | |
| 1440 MemberTypeInformation info = inferrer.types.getInferredTypeOf(element); | |
| 1441 return info.isCalledOnce(); | |
| 1442 } | |
| 1443 | |
| 1444 void clear() { | |
| 1445 inferrer.clear(); | |
| 1446 } | |
| 1447 } | |
| OLD | NEW |