OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import '../common.dart'; |
| 6 import '../common/names.dart'; |
| 7 import '../compiler.dart'; |
| 8 import '../constants/expressions.dart'; |
| 9 import '../constants/values.dart'; |
| 10 import '../elements/elements.dart'; |
| 11 import '../elements/entities.dart'; |
| 12 import '../resolution/tree_elements.dart'; |
| 13 import '../tree/nodes.dart' as ast; |
| 14 import '../types/constants.dart'; |
| 15 import '../types/types.dart'; |
| 16 import '../universe/selector.dart'; |
| 17 import '../util/util.dart'; |
| 18 import '../world.dart'; |
| 19 import 'closure_tracer.dart'; |
| 20 import 'debug.dart' as debug; |
| 21 import 'locals_handler.dart'; |
| 22 import 'builder.dart'; |
| 23 import 'builder_kernel.dart'; |
| 24 import 'inferrer_engine.dart'; |
| 25 import 'type_graph_dump.dart'; |
| 26 import 'type_graph_nodes.dart'; |
| 27 import 'type_system.dart'; |
| 28 |
| 29 class AstInferrerEngine extends InferrerEngineImpl<ast.Node> { |
| 30 AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld, |
| 31 ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement) |
| 32 : super(compiler, closedWorld, closedWorldRefiner, mainElement, |
| 33 const TypeSystemStrategyImpl()); |
| 34 |
| 35 GlobalTypeInferenceElementData<ast.Node> createElementData() => |
| 36 new AstGlobalTypeInferenceElementData(); |
| 37 |
| 38 void runOverAllElements() { |
| 39 if (compiler.disableTypeInference) return; |
| 40 if (compiler.options.verbose) { |
| 41 compiler.progress.reset(); |
| 42 } |
| 43 sortResolvedAsts().forEach((ResolvedAst resolvedAst) { |
| 44 if (compiler.shouldPrintProgress) { |
| 45 reporter.log('Added $addedInGraph elements in inferencing graph.'); |
| 46 compiler.progress.reset(); |
| 47 } |
| 48 // This also forces the creation of the [ElementTypeInformation] to ensure |
| 49 // it is in the graph. |
| 50 MemberElement member = resolvedAst.element; |
| 51 ast.Node body; |
| 52 if (resolvedAst.kind == ResolvedAstKind.PARSED) { |
| 53 body = resolvedAst.body; |
| 54 } |
| 55 types.withMember(member, () => analyze(member, body, null)); |
| 56 }); |
| 57 reporter.log('Added $addedInGraph elements in inferencing graph.'); |
| 58 |
| 59 TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null; |
| 60 |
| 61 dump?.beforeAnalysis(); |
| 62 buildWorkQueue(); |
| 63 refine(); |
| 64 |
| 65 // Try to infer element types of lists and compute their escape information. |
| 66 types.allocatedLists.values.forEach((TypeInformation info) { |
| 67 analyzeListAndEnqueue(info); |
| 68 }); |
| 69 |
| 70 // Try to infer the key and value types for maps and compute the values' |
| 71 // escape information. |
| 72 types.allocatedMaps.values.forEach((TypeInformation info) { |
| 73 analyzeMapAndEnqueue(info); |
| 74 }); |
| 75 |
| 76 Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>(); |
| 77 |
| 78 // Trace closures to potentially infer argument types. |
| 79 types.allocatedClosures.forEach((dynamic info) { |
| 80 void trace( |
| 81 Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) { |
| 82 tracer.run(); |
| 83 if (!tracer.continueAnalyzing) { |
| 84 elements.forEach((FunctionEntity _element) { |
| 85 MethodElement element = _element; |
| 86 MethodElement implementation = element.implementation; |
| 87 closedWorldRefiner.registerMightBePassedToApply(element); |
| 88 if (debug.VERBOSE) { |
| 89 print("traced closure $element as ${true} (bail)"); |
| 90 } |
| 91 implementation.functionSignature |
| 92 .forEachParameter((FormalElement _parameter) { |
| 93 ParameterElement parameter = _parameter; |
| 94 types |
| 95 .getInferredTypeOfParameter(parameter) |
| 96 .giveUp(this, clearAssignments: false); |
| 97 }); |
| 98 }); |
| 99 bailedOutOn.addAll(elements); |
| 100 return; |
| 101 } |
| 102 elements |
| 103 .where((e) => !bailedOutOn.contains(e)) |
| 104 .forEach((FunctionEntity _element) { |
| 105 MethodElement element = _element; |
| 106 MethodElement implementation = element.implementation; |
| 107 implementation.functionSignature |
| 108 .forEachParameter((FormalElement _parameter) { |
| 109 ParameterElement parameter = _parameter; |
| 110 ParameterTypeInformation info = |
| 111 types.getInferredTypeOfParameter(parameter); |
| 112 info.maybeResume(); |
| 113 workQueue.add(info); |
| 114 }); |
| 115 if (tracer.tracedType.mightBePassedToFunctionApply) { |
| 116 closedWorldRefiner.registerMightBePassedToApply(element); |
| 117 } |
| 118 if (debug.VERBOSE) { |
| 119 print("traced closure $element as " |
| 120 "${closedWorldRefiner |
| 121 .getCurrentlyKnownMightBePassedToApply(element)}"); |
| 122 } |
| 123 }); |
| 124 } |
| 125 |
| 126 if (info is ClosureTypeInformation) { |
| 127 Iterable<FunctionEntity> elements = [info.closure]; |
| 128 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 129 } else if (info is CallSiteTypeInformation) { |
| 130 if (info is StaticCallSiteTypeInformation && |
| 131 info.selector != null && |
| 132 info.selector.isCall) { |
| 133 // This is a constructor call to a class with a call method. So we |
| 134 // need to trace the call method here. |
| 135 MethodElement calledElement = info.calledElement; |
| 136 assert(calledElement.isGenerativeConstructor); |
| 137 ClassElement cls = calledElement.enclosingClass; |
| 138 MethodElement callMethod = cls.lookupMember(Identifiers.call); |
| 139 if (callMethod == null) { |
| 140 callMethod = cls.lookupMember(Identifiers.noSuchMethod_); |
| 141 } |
| 142 assert(callMethod != null, failedAt(cls)); |
| 143 Iterable<FunctionEntity> elements = [callMethod]; |
| 144 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 145 } else { |
| 146 // We only are interested in functions here, as other targets |
| 147 // of this closure call are not a root to trace but an intermediate |
| 148 // for some other function. |
| 149 Iterable<FunctionEntity> elements = new List<FunctionEntity>.from( |
| 150 info.callees.where((e) => e.isFunction)); |
| 151 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 152 } |
| 153 } else if (info is MemberTypeInformation) { |
| 154 trace(<FunctionEntity>[info.member], |
| 155 new StaticTearOffClosureTracerVisitor(info.member, info, this)); |
| 156 } else if (info is ParameterTypeInformation) { |
| 157 failedAt( |
| 158 NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info'); |
| 159 } |
| 160 }); |
| 161 |
| 162 dump?.beforeTracing(); |
| 163 |
| 164 // Reset all nodes that use lists/maps that have been inferred, as well |
| 165 // as nodes that use elements fetched from these lists/maps. The |
| 166 // workset for a new run of the analysis will be these nodes. |
| 167 Set<TypeInformation> seenTypes = new Set<TypeInformation>(); |
| 168 while (!workQueue.isEmpty) { |
| 169 TypeInformation info = workQueue.remove(); |
| 170 if (seenTypes.contains(info)) continue; |
| 171 // If the node cannot be reset, we do not need to update its users either. |
| 172 if (!info.reset(this)) continue; |
| 173 seenTypes.add(info); |
| 174 workQueue.addAll(info.users); |
| 175 } |
| 176 |
| 177 workQueue.addAll(seenTypes); |
| 178 refine(); |
| 179 |
| 180 if (debug.PRINT_SUMMARY) { |
| 181 types.allocatedLists.values.forEach((_info) { |
| 182 ListTypeInformation info = _info; |
| 183 print('${info.type} ' |
| 184 'for ${info.originalType.allocationNode} ' |
| 185 'at ${info.originalType.allocationElement} ' |
| 186 'after ${info.refineCount}'); |
| 187 }); |
| 188 types.allocatedMaps.values.forEach((_info) { |
| 189 MapTypeInformation info = _info; |
| 190 print('${info.type} ' |
| 191 'for ${info.originalType.allocationNode} ' |
| 192 'at ${info.originalType.allocationElement} ' |
| 193 'after ${info.refineCount}'); |
| 194 }); |
| 195 types.allocatedClosures.forEach((TypeInformation info) { |
| 196 if (info is ElementTypeInformation) { |
| 197 print('${info.getInferredSignature(types)} for ' |
| 198 '${info.debugName}'); |
| 199 } else if (info is ClosureTypeInformation) { |
| 200 print('${info.getInferredSignature(types)} for ' |
| 201 '${info.debugName}'); |
| 202 } else if (info is DynamicCallSiteTypeInformation) { |
| 203 for (MemberEntity target in info.targets) { |
| 204 if (target is FunctionEntity) { |
| 205 print( |
| 206 '${types.getInferredSignatureOfMethod(target)} for ${target}')
; |
| 207 } else { |
| 208 print( |
| 209 '${types.getInferredTypeOfMember(target).type} for ${target}')
; |
| 210 } |
| 211 } |
| 212 } else if (info is StaticCallSiteTypeInformation) { |
| 213 ClassElement cls = info.calledElement.enclosingClass; |
| 214 MethodElement callMethod = cls.lookupMember(Identifiers.call); |
| 215 print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}'); |
| 216 } else { |
| 217 print('${info.type} for some unknown kind of closure'); |
| 218 } |
| 219 }); |
| 220 analyzedElements.forEach((MemberEntity elem) { |
| 221 TypeInformation type = types.getInferredTypeOfMember(elem); |
| 222 print('${elem} :: ${type} from ${type.assignments} '); |
| 223 }); |
| 224 } |
| 225 dump?.afterAnalysis(); |
| 226 |
| 227 reporter.log('Inferred $overallRefineCount types.'); |
| 228 |
| 229 processLoopInformation(); |
| 230 } |
| 231 |
| 232 void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) { |
| 233 assert(!(element is MemberElement && !element.isDeclaration)); |
| 234 if (analyzedElements.contains(element)) return; |
| 235 analyzedElements.add(element); |
| 236 |
| 237 dynamic visitor = compiler.options.kernelGlobalInference |
| 238 ? new KernelTypeGraphBuilder(element, compiler, this) |
| 239 : new ElementGraphBuilder(element, compiler, this); |
| 240 TypeInformation type; |
| 241 reporter.withCurrentElement(element, () { |
| 242 // ignore: UNDEFINED_METHOD |
| 243 type = visitor.run(); |
| 244 }); |
| 245 addedInGraph++; |
| 246 |
| 247 if (element.isField) { |
| 248 FieldElement field = element; |
| 249 if (field.isFinal || field.isConst) { |
| 250 // If [element] is final and has an initializer, we record |
| 251 // the inferred type. |
| 252 if (body != null) { |
| 253 if (type is! ListTypeInformation && type is! MapTypeInformation) { |
| 254 // For non-container types, the constant handler does |
| 255 // constant folding that could give more precise results. |
| 256 ConstantExpression constant = field.constant; |
| 257 if (constant != null) { |
| 258 ConstantValue value = |
| 259 compiler.backend.constants.getConstantValue(constant); |
| 260 if (value != null) { |
| 261 if (value.isFunction) { |
| 262 FunctionConstantValue functionConstant = value; |
| 263 MethodElement function = functionConstant.element; |
| 264 type = types.allocateClosure(function); |
| 265 } else { |
| 266 // Although we might find a better type, we have to keep |
| 267 // the old type around to ensure that we get a complete view |
| 268 // of the type graph and do not drop any flow edges. |
| 269 TypeMask refinedType = computeTypeMask(closedWorld, value); |
| 270 assert(TypeMask.assertIsNormalized(refinedType, closedWorld)); |
| 271 type = new NarrowTypeInformation(type, refinedType); |
| 272 types.allocatedTypes.add(type); |
| 273 } |
| 274 } else { |
| 275 assert( |
| 276 field.isInstanceMember || |
| 277 constant.isImplicit || |
| 278 constant.isPotential, |
| 279 failedAt( |
| 280 field, |
| 281 "Constant expression without value: " |
| 282 "${constant.toStructuredText()}.")); |
| 283 } |
| 284 } |
| 285 } |
| 286 recordTypeOfField(field, type); |
| 287 } else if (!element.isInstanceMember) { |
| 288 recordTypeOfField(field, types.nullType); |
| 289 } |
| 290 } else if (body == null) { |
| 291 // Only update types of static fields if there is no |
| 292 // assignment. Instance fields are dealt with in the constructor. |
| 293 if (element.isStatic || element.isTopLevel) { |
| 294 recordTypeOfField(field, type); |
| 295 } |
| 296 } else { |
| 297 recordTypeOfField(field, type); |
| 298 } |
| 299 if ((element.isStatic || element.isTopLevel) && |
| 300 body != null && |
| 301 !element.isConst) { |
| 302 dynamic argument = body; |
| 303 // TODO(13429): We could do better here by using the |
| 304 // constant handler to figure out if it's a lazy field or not. |
| 305 if (argument.asSend() != null || |
| 306 (argument.asNewExpression() != null && !argument.isConst)) { |
| 307 recordTypeOfField(field, types.nullType); |
| 308 } |
| 309 } |
| 310 } else { |
| 311 FunctionEntity method = element; |
| 312 recordReturnType(method, type); |
| 313 } |
| 314 } |
| 315 |
| 316 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, |
| 317 ArgumentsTypes arguments, Selector selector, TypeMask mask, |
| 318 {bool remove, bool addToQueue: true}) { |
| 319 if (callee.name == Identifiers.noSuchMethod_) return; |
| 320 if (callee.isField) { |
| 321 if (selector.isSetter) { |
| 322 ElementTypeInformation info = types.getInferredTypeOfMember(callee); |
| 323 if (remove) { |
| 324 info.removeAssignment(arguments.positional[0]); |
| 325 } else { |
| 326 info.addAssignment(arguments.positional[0]); |
| 327 } |
| 328 if (addToQueue) workQueue.add(info); |
| 329 } |
| 330 } else if (callee.isGetter) { |
| 331 return; |
| 332 } else if (selector != null && selector.isGetter) { |
| 333 // We are tearing a function off and thus create a closure. |
| 334 assert(callee.isFunction); |
| 335 MethodElement method = callee; |
| 336 MemberTypeInformation info = types.getInferredTypeOfMember(method); |
| 337 if (remove) { |
| 338 info.closurizedCount--; |
| 339 } else { |
| 340 info.closurizedCount++; |
| 341 if (Elements.isStaticOrTopLevel(method)) { |
| 342 types.allocatedClosures.add(info); |
| 343 } else { |
| 344 // We add the call-site type information here so that we |
| 345 // can benefit from further refinement of the selector. |
| 346 types.allocatedClosures.add(caller); |
| 347 } |
| 348 FunctionElement function = method.implementation; |
| 349 FunctionSignature signature = function.functionSignature; |
| 350 signature.forEachParameter((FormalElement _parameter) { |
| 351 ParameterElement parameter = _parameter; |
| 352 ParameterTypeInformation info = |
| 353 types.getInferredTypeOfParameter(parameter); |
| 354 info.tagAsTearOffClosureParameter(this); |
| 355 if (addToQueue) workQueue.add(info); |
| 356 }); |
| 357 } |
| 358 } else { |
| 359 MethodElement method = callee; |
| 360 FunctionElement function = method.implementation; |
| 361 FunctionSignature signature = function.functionSignature; |
| 362 int parameterIndex = 0; |
| 363 bool visitingRequiredParameter = true; |
| 364 signature.forEachParameter((FormalElement _parameter) { |
| 365 ParameterElement parameter = _parameter; |
| 366 if (signature.hasOptionalParameters && |
| 367 parameter == signature.optionalParameters.first) { |
| 368 visitingRequiredParameter = false; |
| 369 } |
| 370 TypeInformation type = visitingRequiredParameter |
| 371 ? arguments.positional[parameterIndex] |
| 372 : signature.optionalParametersAreNamed |
| 373 ? arguments.named[parameter.name] |
| 374 : parameterIndex < arguments.positional.length |
| 375 ? arguments.positional[parameterIndex] |
| 376 : null; |
| 377 if (type == null) type = getDefaultTypeOfParameter(parameter); |
| 378 TypeInformation info = types.getInferredTypeOfParameter(parameter); |
| 379 if (remove) { |
| 380 info.removeAssignment(type); |
| 381 } else { |
| 382 info.addAssignment(type); |
| 383 } |
| 384 parameterIndex++; |
| 385 if (addToQueue) workQueue.add(info); |
| 386 }); |
| 387 } |
| 388 } |
| 389 |
| 390 // Sorts the resolved elements by size. We do this for this inferrer |
| 391 // to get the same results for [ListTracer] compared to the |
| 392 // [SimpleTypesInferrer]. |
| 393 Iterable<ResolvedAst> sortResolvedAsts() { |
| 394 int max = 0; |
| 395 Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{}; |
| 396 compiler.enqueuer.resolution.processedEntities.forEach((_element) { |
| 397 MemberElement element = _element; |
| 398 ResolvedAst resolvedAst = element.resolvedAst; |
| 399 element = element.implementation; |
| 400 if (element.impliesType) return; |
| 401 assert( |
| 402 element.isField || |
| 403 element.isFunction || |
| 404 element.isConstructor || |
| 405 element.isGetter || |
| 406 element.isSetter, |
| 407 failedAt(element, 'Unexpected element kind: ${element.kind}')); |
| 408 if (element.isAbstract) return; |
| 409 // Put the other operators in buckets by length, later to be added in |
| 410 // length order. |
| 411 int length = 0; |
| 412 if (resolvedAst.kind == ResolvedAstKind.PARSED) { |
| 413 TreeElementMapping mapping = resolvedAst.elements; |
| 414 length = mapping.getSelectorCount(); |
| 415 } |
| 416 max = length > max ? length : max; |
| 417 Setlet<ResolvedAst> set = |
| 418 methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>()); |
| 419 set.add(resolvedAst); |
| 420 }); |
| 421 |
| 422 List<ResolvedAst> result = <ResolvedAst>[]; |
| 423 for (int i = 0; i <= max; i++) { |
| 424 Setlet<ResolvedAst> set = methodSizes[i]; |
| 425 if (set != null) result.addAll(set); |
| 426 } |
| 427 return result; |
| 428 } |
| 429 } |
| 430 |
| 431 class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> { |
| 432 const TypeSystemStrategyImpl(); |
| 433 |
| 434 @override |
| 435 MemberTypeInformation createMemberTypeInformation( |
| 436 covariant MemberElement member) { |
| 437 assert(member.isDeclaration, failedAt(member)); |
| 438 if (member.isField) { |
| 439 FieldElement field = member; |
| 440 return new FieldTypeInformation(field, field.type); |
| 441 } else if (member.isGetter) { |
| 442 GetterElement getter = member; |
| 443 return new GetterTypeInformation(getter, getter.type); |
| 444 } else if (member.isSetter) { |
| 445 SetterElement setter = member; |
| 446 return new SetterTypeInformation(setter); |
| 447 } else if (member.isFunction) { |
| 448 MethodElement method = member; |
| 449 return new MethodTypeInformation(method, method.type); |
| 450 } else { |
| 451 ConstructorElement constructor = member; |
| 452 if (constructor.isFactoryConstructor) { |
| 453 return new FactoryConstructorTypeInformation( |
| 454 constructor, constructor.type); |
| 455 } else { |
| 456 return new GenerativeConstructorTypeInformation(constructor); |
| 457 } |
| 458 } |
| 459 } |
| 460 |
| 461 @override |
| 462 ParameterTypeInformation createParameterTypeInformation( |
| 463 covariant ParameterElement parameter, TypeSystem<ast.Node> types) { |
| 464 assert(parameter.isImplementation, failedAt(parameter)); |
| 465 FunctionTypedElement function = parameter.functionDeclaration.declaration; |
| 466 if (function.isLocal) { |
| 467 LocalFunctionElement localFunction = function; |
| 468 MethodElement callMethod = localFunction.callMethod; |
| 469 return new ParameterTypeInformation.localFunction( |
| 470 types.getInferredTypeOfMember(callMethod), |
| 471 parameter, |
| 472 parameter.type, |
| 473 callMethod); |
| 474 } else if (function.isInstanceMember) { |
| 475 MethodElement method = function; |
| 476 return new ParameterTypeInformation.instanceMember( |
| 477 types.getInferredTypeOfMember(method), |
| 478 parameter, |
| 479 parameter.type, |
| 480 method, |
| 481 new ParameterAssignments()); |
| 482 } else { |
| 483 MethodElement method = function; |
| 484 return new ParameterTypeInformation.static( |
| 485 types.getInferredTypeOfMember(method), |
| 486 parameter, |
| 487 parameter.type, |
| 488 method, |
| 489 // TODO(johnniwinther): Is this still valid now that initializing |
| 490 // formals also introduce locals? |
| 491 isInitializingFormal: parameter.isInitializingFormal); |
| 492 } |
| 493 } |
| 494 |
| 495 @override |
| 496 void forEachParameter( |
| 497 covariant MethodElement function, void f(Local parameter)) { |
| 498 MethodElement impl = function.implementation; |
| 499 FunctionSignature signature = impl.functionSignature; |
| 500 signature.forEachParameter((FormalElement _parameter) { |
| 501 ParameterElement parameter = _parameter; |
| 502 f(parameter); |
| 503 }); |
| 504 } |
| 505 |
| 506 @override |
| 507 bool checkMapNode(ast.Node node) { |
| 508 return node is ast.LiteralMap; |
| 509 } |
| 510 |
| 511 @override |
| 512 bool checkListNode(ast.Node node) { |
| 513 return node is ast.LiteralList || node is ast.Send; |
| 514 } |
| 515 |
| 516 @override |
| 517 bool checkLoopPhiNode(ast.Node node) { |
| 518 return node is ast.Loop || node is ast.SwitchStatement; |
| 519 } |
| 520 |
| 521 @override |
| 522 bool checkPhiNode(ast.Node node) { |
| 523 return true; |
| 524 } |
| 525 |
| 526 @override |
| 527 bool checkClassEntity(covariant ClassElement cls) { |
| 528 return cls.isDeclaration; |
| 529 } |
| 530 } |
OLD | NEW |