| OLD | NEW |
| 1 // Copyright (c) 2017, 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 import 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../common/names.dart'; | 8 import '../common/names.dart'; |
| 9 import '../compiler.dart'; | 9 import '../compiler.dart'; |
| 10 import '../common_elements.dart'; | 10 import '../common_elements.dart'; |
| 11 import '../constants/values.dart'; |
| 11 import '../elements/elements.dart' | 12 import '../elements/elements.dart' |
| 12 show | 13 show |
| 13 ClassElement, | 14 ClassElement, |
| 14 ConstructorElement, | 15 ConstructorElement, |
| 15 Elements, | 16 Elements, |
| 16 MemberElement, | 17 MemberElement, |
| 17 ParameterElement; | 18 ParameterElement; |
| 18 import '../elements/entities.dart'; | 19 import '../elements/entities.dart'; |
| 19 import '../elements/names.dart'; | 20 import '../elements/names.dart'; |
| 20 import '../js_backend/annotations.dart'; | 21 import '../js_backend/annotations.dart'; |
| 21 import '../js_backend/js_backend.dart'; | 22 import '../js_backend/js_backend.dart'; |
| 22 import '../native/behavior.dart' as native; | 23 import '../native/behavior.dart' as native; |
| 24 import '../types/constants.dart'; |
| 23 import '../types/types.dart'; | 25 import '../types/types.dart'; |
| 24 import '../universe/call_structure.dart'; | 26 import '../universe/call_structure.dart'; |
| 25 import '../universe/selector.dart'; | 27 import '../universe/selector.dart'; |
| 26 import '../universe/side_effects.dart'; | 28 import '../universe/side_effects.dart'; |
| 27 import '../world.dart'; | 29 import '../world.dart'; |
| 30 import 'closure_tracer.dart'; |
| 28 import 'debug.dart' as debug; | 31 import 'debug.dart' as debug; |
| 29 import 'locals_handler.dart'; | 32 import 'locals_handler.dart'; |
| 30 import 'list_tracer.dart'; | 33 import 'list_tracer.dart'; |
| 31 import 'map_tracer.dart'; | 34 import 'map_tracer.dart'; |
| 32 import 'builder.dart'; | 35 import 'builder.dart'; |
| 36 import 'type_graph_dump.dart'; |
| 33 import 'type_graph_inferrer.dart'; | 37 import 'type_graph_inferrer.dart'; |
| 34 import 'type_graph_nodes.dart'; | 38 import 'type_graph_nodes.dart'; |
| 35 import 'type_system.dart'; | 39 import 'type_system.dart'; |
| 36 | 40 |
| 37 /// An inferencing engine that computes a call graph of [TypeInformation] nodes | 41 /// An inferencing engine that computes a call graph of [TypeInformation] nodes |
| 38 /// by visiting the AST of the application, and then does the inferencing on the | 42 /// by visiting the AST of the application, and then does the inferencing on the |
| 39 /// graph. | 43 /// graph. |
| 40 abstract class InferrerEngine<T> { | 44 abstract class InferrerEngine<T> { |
| 41 /// A set of selector names that [List] implements, that we know return their | 45 /// A set of selector names that [List] implements, that we know return their |
| 42 /// element type. | 46 /// element type. |
| (...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 workQueue.addAll(info.addMapAssignment(map)); | 453 workQueue.addAll(info.addMapAssignment(map)); |
| 450 } | 454 } |
| 451 | 455 |
| 452 info.markAsInferred(); | 456 info.markAsInferred(); |
| 453 workQueue.add(info.keyType); | 457 workQueue.add(info.keyType); |
| 454 workQueue.add(info.valueType); | 458 workQueue.add(info.valueType); |
| 455 workQueue.addAll(info.typeInfoMap.values); | 459 workQueue.addAll(info.typeInfoMap.values); |
| 456 workQueue.add(info); | 460 workQueue.add(info); |
| 457 } | 461 } |
| 458 | 462 |
| 459 void runOverAllElements(); | 463 void runOverAllElements() { |
| 460 | 464 if (compiler.disableTypeInference) return; |
| 461 void analyze(MemberEntity element, T body, ArgumentsTypes arguments); | 465 if (compiler.options.verbose) { |
| 466 compiler.progress.reset(); |
| 467 } |
| 468 analyzeAllElements(); |
| 469 |
| 470 TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null; |
| 471 |
| 472 dump?.beforeAnalysis(); |
| 473 buildWorkQueue(); |
| 474 refine(); |
| 475 |
| 476 // Try to infer element types of lists and compute their escape information. |
| 477 types.allocatedLists.values.forEach((TypeInformation info) { |
| 478 analyzeListAndEnqueue(info); |
| 479 }); |
| 480 |
| 481 // Try to infer the key and value types for maps and compute the values' |
| 482 // escape information. |
| 483 types.allocatedMaps.values.forEach((TypeInformation info) { |
| 484 analyzeMapAndEnqueue(info); |
| 485 }); |
| 486 |
| 487 Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>(); |
| 488 |
| 489 // Trace closures to potentially infer argument types. |
| 490 types.allocatedClosures.forEach((dynamic info) { |
| 491 void trace( |
| 492 Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) { |
| 493 tracer.run(); |
| 494 if (!tracer.continueAnalyzing) { |
| 495 elements.forEach((FunctionEntity element) { |
| 496 closedWorldRefiner.registerMightBePassedToApply(element); |
| 497 if (debug.VERBOSE) { |
| 498 print("traced closure $element as ${true} (bail)"); |
| 499 } |
| 500 forEachParameter(element, (Local parameter) { |
| 501 types |
| 502 .getInferredTypeOfParameter(parameter) |
| 503 .giveUp(this, clearAssignments: false); |
| 504 }); |
| 505 }); |
| 506 bailedOutOn.addAll(elements); |
| 507 return; |
| 508 } |
| 509 elements |
| 510 .where((e) => !bailedOutOn.contains(e)) |
| 511 .forEach((FunctionEntity element) { |
| 512 forEachParameter(element, (Local parameter) { |
| 513 ParameterTypeInformation info = |
| 514 types.getInferredTypeOfParameter(parameter); |
| 515 info.maybeResume(); |
| 516 workQueue.add(info); |
| 517 }); |
| 518 if (tracer.tracedType.mightBePassedToFunctionApply) { |
| 519 closedWorldRefiner.registerMightBePassedToApply(element); |
| 520 } |
| 521 if (debug.VERBOSE) { |
| 522 print("traced closure $element as " |
| 523 "${closedWorldRefiner |
| 524 .getCurrentlyKnownMightBePassedToApply(element)}"); |
| 525 } |
| 526 }); |
| 527 } |
| 528 |
| 529 if (info is ClosureTypeInformation) { |
| 530 Iterable<FunctionEntity> elements = [info.closure]; |
| 531 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 532 } else if (info is CallSiteTypeInformation) { |
| 533 if (info is StaticCallSiteTypeInformation && |
| 534 info.selector != null && |
| 535 info.selector.isCall) { |
| 536 // This is a constructor call to a class with a call method. So we |
| 537 // need to trace the call method here. |
| 538 FunctionEntity calledElement = info.calledElement; |
| 539 assert(calledElement is ConstructorEntity && |
| 540 calledElement.isGenerativeConstructor); |
| 541 ClassEntity cls = calledElement.enclosingClass; |
| 542 FunctionEntity callMethod = lookupCallMethod(cls); |
| 543 assert(callMethod != null, failedAt(cls)); |
| 544 Iterable<FunctionEntity> elements = [callMethod]; |
| 545 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 546 } else { |
| 547 // We only are interested in functions here, as other targets |
| 548 // of this closure call are not a root to trace but an intermediate |
| 549 // for some other function. |
| 550 Iterable<FunctionEntity> elements = new List<FunctionEntity>.from( |
| 551 info.callees.where((e) => e.isFunction)); |
| 552 trace(elements, new ClosureTracerVisitor(elements, info, this)); |
| 553 } |
| 554 } else if (info is MemberTypeInformation) { |
| 555 trace(<FunctionEntity>[info.member], |
| 556 new StaticTearOffClosureTracerVisitor(info.member, info, this)); |
| 557 } else if (info is ParameterTypeInformation) { |
| 558 failedAt( |
| 559 NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info'); |
| 560 } |
| 561 }); |
| 562 |
| 563 dump?.beforeTracing(); |
| 564 |
| 565 // Reset all nodes that use lists/maps that have been inferred, as well |
| 566 // as nodes that use elements fetched from these lists/maps. The |
| 567 // workset for a new run of the analysis will be these nodes. |
| 568 Set<TypeInformation> seenTypes = new Set<TypeInformation>(); |
| 569 while (!workQueue.isEmpty) { |
| 570 TypeInformation info = workQueue.remove(); |
| 571 if (seenTypes.contains(info)) continue; |
| 572 // If the node cannot be reset, we do not need to update its users either. |
| 573 if (!info.reset(this)) continue; |
| 574 seenTypes.add(info); |
| 575 workQueue.addAll(info.users); |
| 576 } |
| 577 |
| 578 workQueue.addAll(seenTypes); |
| 579 refine(); |
| 580 |
| 581 if (debug.PRINT_SUMMARY) { |
| 582 types.allocatedLists.values.forEach((_info) { |
| 583 ListTypeInformation info = _info; |
| 584 print('${info.type} ' |
| 585 'for ${info.originalType.allocationNode} ' |
| 586 'at ${info.originalType.allocationElement} ' |
| 587 'after ${info.refineCount}'); |
| 588 }); |
| 589 types.allocatedMaps.values.forEach((_info) { |
| 590 MapTypeInformation info = _info; |
| 591 print('${info.type} ' |
| 592 'for ${info.originalType.allocationNode} ' |
| 593 'at ${info.originalType.allocationElement} ' |
| 594 'after ${info.refineCount}'); |
| 595 }); |
| 596 types.allocatedClosures.forEach((TypeInformation info) { |
| 597 if (info is ElementTypeInformation) { |
| 598 print('${info.getInferredSignature(types)} for ' |
| 599 '${info.debugName}'); |
| 600 } else if (info is ClosureTypeInformation) { |
| 601 print('${info.getInferredSignature(types)} for ' |
| 602 '${info.debugName}'); |
| 603 } else if (info is DynamicCallSiteTypeInformation) { |
| 604 for (MemberEntity target in info.targets) { |
| 605 if (target is FunctionEntity) { |
| 606 print( |
| 607 '${types.getInferredSignatureOfMethod(target)} for ${target}')
; |
| 608 } else { |
| 609 print( |
| 610 '${types.getInferredTypeOfMember(target).type} for ${target}')
; |
| 611 } |
| 612 } |
| 613 } else if (info is StaticCallSiteTypeInformation) { |
| 614 ClassEntity cls = info.calledElement.enclosingClass; |
| 615 FunctionEntity callMethod = lookupCallMethod(cls); |
| 616 print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}'); |
| 617 } else { |
| 618 print('${info.type} for some unknown kind of closure'); |
| 619 } |
| 620 }); |
| 621 analyzedElements.forEach((MemberEntity elem) { |
| 622 TypeInformation type = types.getInferredTypeOfMember(elem); |
| 623 print('${elem} :: ${type} from ${type.assignments} '); |
| 624 }); |
| 625 } |
| 626 dump?.afterAnalysis(); |
| 627 |
| 628 reporter.log('Inferred $overallRefineCount types.'); |
| 629 |
| 630 processLoopInformation(); |
| 631 } |
| 632 |
| 633 /// Call [analyze] for all live members. |
| 634 void analyzeAllElements(); |
| 635 |
| 636 /// Calls [f] for each parameter of [method]. |
| 637 void forEachParameter(FunctionEntity method, void f(Local parameter)); |
| 638 |
| 639 /// Returns the `call` method on [cls] or the `noSuchMethod` if [cls] doesn't |
| 640 /// implement `call`. |
| 641 FunctionEntity lookupCallMethod(ClassEntity cls); |
| 642 |
| 643 void analyze(MemberEntity element, T body, ArgumentsTypes arguments) { |
| 644 assert(!(element is MemberElement && !element.isDeclaration)); |
| 645 if (analyzedElements.contains(element)) return; |
| 646 analyzedElements.add(element); |
| 647 |
| 648 TypeInformation type; |
| 649 reporter.withCurrentElement(element, () { |
| 650 type = computeMemberTypeInformation(element, body); |
| 651 }); |
| 652 addedInGraph++; |
| 653 |
| 654 if (element.isField) { |
| 655 FieldEntity field = element; |
| 656 if (!field.isAssignable) { |
| 657 // If [element] is final and has an initializer, we record |
| 658 // the inferred type. |
| 659 if (body != null) { |
| 660 if (type is! ListTypeInformation && type is! MapTypeInformation) { |
| 661 // For non-container types, the constant handler does |
| 662 // constant folding that could give more precise results. |
| 663 ConstantValue value = getFieldConstant(field); |
| 664 if (value != null) { |
| 665 if (value.isFunction) { |
| 666 FunctionConstantValue functionConstant = value; |
| 667 FunctionEntity function = functionConstant.element; |
| 668 type = types.allocateClosure(function); |
| 669 } else { |
| 670 // Although we might find a better type, we have to keep |
| 671 // the old type around to ensure that we get a complete view |
| 672 // of the type graph and do not drop any flow edges. |
| 673 TypeMask refinedType = computeTypeMask(closedWorld, value); |
| 674 assert(TypeMask.assertIsNormalized(refinedType, closedWorld)); |
| 675 type = new NarrowTypeInformation(type, refinedType); |
| 676 types.allocatedTypes.add(type); |
| 677 } |
| 678 } |
| 679 } |
| 680 recordTypeOfField(field, type); |
| 681 } else if (!element.isInstanceMember) { |
| 682 recordTypeOfField(field, types.nullType); |
| 683 } |
| 684 } else if (body == null) { |
| 685 // Only update types of static fields if there is no |
| 686 // assignment. Instance fields are dealt with in the constructor. |
| 687 if (element.isStatic || element.isTopLevel) { |
| 688 recordTypeOfField(field, type); |
| 689 } |
| 690 } else { |
| 691 recordTypeOfField(field, type); |
| 692 } |
| 693 if ((element.isStatic || element.isTopLevel) && |
| 694 body != null && |
| 695 !element.isConst) { |
| 696 if (isFieldInitializerPotentiallyNull(element, body)) { |
| 697 recordTypeOfField(field, types.nullType); |
| 698 } |
| 699 } |
| 700 } else { |
| 701 FunctionEntity method = element; |
| 702 recordReturnType(method, type); |
| 703 } |
| 704 } |
| 705 |
| 706 /// Visits [body] to compute the [TypeInformation] node for [member]. |
| 707 TypeInformation computeMemberTypeInformation(MemberEntity member, T body); |
| 708 |
| 709 /// Returns `true` if the [initializer] of the non-const static or top-level |
| 710 /// [field] is potentially `null`. |
| 711 bool isFieldInitializerPotentiallyNull(FieldEntity field, T initializer); |
| 712 |
| 713 /// Returns the [ConstantValue] for the initial value of [field], or |
| 714 /// `null` if the initializer is not a constant value. |
| 715 ConstantValue getFieldConstant(FieldEntity field); |
| 462 | 716 |
| 463 void processLoopInformation() { | 717 void processLoopInformation() { |
| 464 types.allocatedCalls.forEach((dynamic info) { | 718 types.allocatedCalls.forEach((dynamic info) { |
| 465 if (!info.inLoop) return; | 719 if (!info.inLoop) return; |
| 466 if (info is StaticCallSiteTypeInformation) { | 720 if (info is StaticCallSiteTypeInformation) { |
| 467 MemberEntity member = info.calledElement; | 721 MemberEntity member = info.calledElement; |
| 468 closedWorldRefiner.addFunctionCalledInLoop(member); | 722 closedWorldRefiner.addFunctionCalledInLoop(member); |
| 469 } else if (info.mask != null && !info.mask.containsAll(closedWorld)) { | 723 } else if (info.mask != null && !info.mask.containsAll(closedWorld)) { |
| 470 // For instance methods, we only register a selector called in a | 724 // For instance methods, we only register a selector called in a |
| 471 // loop if it is a typed selector, to avoid marking too many | 725 // loop if it is a typed selector, to avoid marking too many |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 | 765 |
| 512 void buildWorkQueue() { | 766 void buildWorkQueue() { |
| 513 workQueue.addAll(types.orderedTypeInformations); | 767 workQueue.addAll(types.orderedTypeInformations); |
| 514 workQueue.addAll(types.allocatedTypes); | 768 workQueue.addAll(types.allocatedTypes); |
| 515 workQueue.addAll(types.allocatedClosures); | 769 workQueue.addAll(types.allocatedClosures); |
| 516 workQueue.addAll(types.allocatedCalls); | 770 workQueue.addAll(types.allocatedCalls); |
| 517 } | 771 } |
| 518 | 772 |
| 519 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, | 773 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, |
| 520 ArgumentsTypes arguments, Selector selector, TypeMask mask, | 774 ArgumentsTypes arguments, Selector selector, TypeMask mask, |
| 521 {bool remove, bool addToQueue: true}); | 775 {bool remove, bool addToQueue: true}) { |
| 776 if (callee.name == Identifiers.noSuchMethod_) return; |
| 777 if (callee.isField) { |
| 778 if (selector.isSetter) { |
| 779 ElementTypeInformation info = types.getInferredTypeOfMember(callee); |
| 780 if (remove) { |
| 781 info.removeAssignment(arguments.positional[0]); |
| 782 } else { |
| 783 info.addAssignment(arguments.positional[0]); |
| 784 } |
| 785 if (addToQueue) workQueue.add(info); |
| 786 } |
| 787 } else if (callee.isGetter) { |
| 788 return; |
| 789 } else if (selector != null && selector.isGetter) { |
| 790 // We are tearing a function off and thus create a closure. |
| 791 assert(callee.isFunction); |
| 792 MemberTypeInformation info = types.getInferredTypeOfMember(callee); |
| 793 if (remove) { |
| 794 info.closurizedCount--; |
| 795 } else { |
| 796 info.closurizedCount++; |
| 797 if (callee.isStatic || callee.isTopLevel) { |
| 798 types.allocatedClosures.add(info); |
| 799 } else { |
| 800 // We add the call-site type information here so that we |
| 801 // can benefit from further refinement of the selector. |
| 802 types.allocatedClosures.add(caller); |
| 803 } |
| 804 forEachParameter(callee, (Local parameter) { |
| 805 ParameterTypeInformation info = |
| 806 types.getInferredTypeOfParameter(parameter); |
| 807 info.tagAsTearOffClosureParameter(this); |
| 808 if (addToQueue) workQueue.add(info); |
| 809 }); |
| 810 } |
| 811 } else { |
| 812 FunctionEntity method = callee; |
| 813 ParameterStructure parameterStructure = method.parameterStructure; |
| 814 int parameterIndex = 0; |
| 815 forEachParameter(callee, (Local parameter) { |
| 816 TypeInformation type; |
| 817 if (parameterIndex < parameterStructure.requiredParameters) { |
| 818 type = arguments.positional[parameterIndex]; |
| 819 } else if (parameterStructure.namedParameters.isNotEmpty) { |
| 820 type = arguments.named[parameter.name]; |
| 821 } else if (parameterIndex < arguments.positional.length) { |
| 822 type = arguments.positional[parameterIndex]; |
| 823 } |
| 824 if (type == null) type = getDefaultTypeOfParameter(parameter); |
| 825 TypeInformation info = types.getInferredTypeOfParameter(parameter); |
| 826 if (remove) { |
| 827 info.removeAssignment(type); |
| 828 } else { |
| 829 info.addAssignment(type); |
| 830 } |
| 831 parameterIndex++; |
| 832 if (addToQueue) workQueue.add(info); |
| 833 }); |
| 834 } |
| 835 } |
| 522 | 836 |
| 523 void setDefaultTypeOfParameter(Local parameter, TypeInformation type, | 837 void setDefaultTypeOfParameter(Local parameter, TypeInformation type, |
| 524 {bool isInstanceMember}) { | 838 {bool isInstanceMember}) { |
| 525 assert(!(parameter is ParameterElement && !parameter.isImplementation)); | 839 assert(!(parameter is ParameterElement && !parameter.isImplementation)); |
| 526 TypeInformation existing = defaultTypeOfParameter[parameter]; | 840 TypeInformation existing = defaultTypeOfParameter[parameter]; |
| 527 defaultTypeOfParameter[parameter] = type; | 841 defaultTypeOfParameter[parameter] = type; |
| 528 TypeInformation info = types.getInferredTypeOfParameter(parameter); | 842 TypeInformation info = types.getInferredTypeOfParameter(parameter); |
| 529 if (existing != null && existing is PlaceholderTypeInformation) { | 843 if (existing != null && existing is PlaceholderTypeInformation) { |
| 530 // Replace references to [existing] to use [type] instead. | 844 // Replace references to [existing] to use [type] instead. |
| 531 if (isInstanceMember) { | 845 if (isInstanceMember) { |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 774 return returnTypeOfMember(element); | 1088 return returnTypeOfMember(element); |
| 775 } | 1089 } |
| 776 } else if (element.isGetter || element.isField) { | 1090 } else if (element.isGetter || element.isField) { |
| 777 assert(selector.isCall || selector.isSetter); | 1091 assert(selector.isCall || selector.isSetter); |
| 778 return types.dynamicType; | 1092 return types.dynamicType; |
| 779 } else { | 1093 } else { |
| 780 return returnTypeOfMember(element); | 1094 return returnTypeOfMember(element); |
| 781 } | 1095 } |
| 782 } | 1096 } |
| 783 } | 1097 } |
| OLD | NEW |