| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 class Constant implements Hashable { | 5 class Constant implements Hashable { |
| 6 const Constant(); | 6 const Constant(); |
| 7 | 7 |
| 8 bool isNull() => false; | 8 bool isNull() => false; |
| 9 bool isBool() => false; | 9 bool isBool() => false; |
| 10 bool isTrue() => false; | 10 bool isTrue() => false; |
| (...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 518 if (fields.length != other.fields.length) return false; | 518 if (fields.length != other.fields.length) return false; |
| 519 for (int i = 0; i < fields.length; i++) { | 519 for (int i = 0; i < fields.length; i++) { |
| 520 if (fields[i] != other.fields[i]) return false; | 520 if (fields[i] != other.fields[i]) return false; |
| 521 } | 521 } |
| 522 return true; | 522 return true; |
| 523 } | 523 } |
| 524 | 524 |
| 525 int hashCode() => _hashCode; | 525 int hashCode() => _hashCode; |
| 526 List<Constant> getDependencies() => fields; | 526 List<Constant> getDependencies() => fields; |
| 527 } | 527 } |
| 528 | |
| 529 /** | |
| 530 * The [ConstantHandler] keeps track of compile-time constants, | |
| 531 * initializations of global and static fields, and default values of | |
| 532 * optional parameters. | |
| 533 */ | |
| 534 class ConstantHandler extends CompilerTask { | |
| 535 final ConstantSystem constantSystem; | |
| 536 | |
| 537 /** | |
| 538 * Contains the initial value of fields. Must contain all static and global | |
| 539 * initializations of const fields. May contain eagerly compiled values for | |
| 540 * statics and instance fields. | |
| 541 */ | |
| 542 final Map<VariableElement, Constant> initialVariableValues; | |
| 543 | |
| 544 /** Map from compile-time constants to their JS name. */ | |
| 545 final Map<Constant, String> compiledConstants; | |
| 546 | |
| 547 /** The set of variable elements that are in the process of being computed. */ | |
| 548 final Set<VariableElement> pendingVariables; | |
| 549 | |
| 550 /** Caches the statics where the initial value cannot be eagerly compiled. */ | |
| 551 final Set<VariableElement> lazyStatics; | |
| 552 | |
| 553 | |
| 554 ConstantHandler(Compiler compiler, this.constantSystem) | |
| 555 : initialVariableValues = new Map<VariableElement, Dynamic>(), | |
| 556 compiledConstants = new Map<Constant, String>(), | |
| 557 pendingVariables = new Set<VariableElement>(), | |
| 558 lazyStatics = new Set<VariableElement>(), | |
| 559 super(compiler); | |
| 560 String get name => 'ConstantHandler'; | |
| 561 | |
| 562 void registerCompileTimeConstant(Constant constant) { | |
| 563 Function ifAbsentThunk = (() { | |
| 564 return constant.isFunction() | |
| 565 ? null : compiler.namer.getFreshGlobalName("CTC"); | |
| 566 }); | |
| 567 compiledConstants.putIfAbsent(constant, ifAbsentThunk); | |
| 568 } | |
| 569 | |
| 570 /** | |
| 571 * Compiles the initial value of the given field and stores it in an internal | |
| 572 * map. Returns the initial value (a constant) if it can be computed | |
| 573 * statically. Returns [:null:] if the variable must be initialized lazily. | |
| 574 * | |
| 575 * [WorkItem] must contain a [VariableElement] refering to a global or | |
| 576 * static field. | |
| 577 */ | |
| 578 Constant compileWorkItem(WorkItem work) { | |
| 579 return measure(() { | |
| 580 assert(work.element.kind == ElementKind.FIELD | |
| 581 || work.element.kind == ElementKind.PARAMETER | |
| 582 || work.element.kind == ElementKind.FIELD_PARAMETER); | |
| 583 VariableElement element = work.element; | |
| 584 // Shortcut if it has already been compiled. | |
| 585 Constant result = initialVariableValues[element]; | |
| 586 if (result != null) return result; | |
| 587 if (lazyStatics.contains(element)) return null; | |
| 588 result = compileVariableWithDefinitions(element, work.resolutionTree); | |
| 589 assert(pendingVariables.isEmpty()); | |
| 590 return result; | |
| 591 }); | |
| 592 } | |
| 593 | |
| 594 /** | |
| 595 * Returns a compile-time constant, or reports an error if the element is not | |
| 596 * a compile-time constant. | |
| 597 */ | |
| 598 Constant compileConstant(VariableElement element) { | |
| 599 return compileVariable(element, isConst: true); | |
| 600 } | |
| 601 | |
| 602 /** | |
| 603 * Returns the a compile-time constant if the variable could be compiled | |
| 604 * eagerly. Otherwise returns `null`. | |
| 605 */ | |
| 606 Constant compileVariable(VariableElement element, [bool isConst = false]) { | |
| 607 return measure(() { | |
| 608 if (initialVariableValues.containsKey(element)) { | |
| 609 Constant result = initialVariableValues[element]; | |
| 610 return result; | |
| 611 } | |
| 612 TreeElements definitions = compiler.analyzeElement(element); | |
| 613 Constant constant = compileVariableWithDefinitions( | |
| 614 element, definitions, isConst: isConst); | |
| 615 return constant; | |
| 616 }); | |
| 617 } | |
| 618 | |
| 619 /** | |
| 620 * Returns the a compile-time constant if the variable could be compiled | |
| 621 * eagerly. If the variable needs to be initialized lazily returns `null`. | |
| 622 * If the variable is `const` but cannot be compiled eagerly reports an | |
| 623 * error. | |
| 624 */ | |
| 625 Constant compileVariableWithDefinitions(VariableElement element, | |
| 626 TreeElements definitions, | |
| 627 [bool isConst = false]) { | |
| 628 return measure(() { | |
| 629 // Initializers for parameters must be const. | |
| 630 isConst = isConst || element.modifiers.isConst() | |
| 631 || !Elements.isStaticOrTopLevel(element); | |
| 632 if (!isConst && lazyStatics.contains(element)) return null; | |
| 633 | |
| 634 Node node = element.parseNode(compiler); | |
| 635 if (pendingVariables.contains(element)) { | |
| 636 if (isConst) { | |
| 637 MessageKind kind = MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS; | |
| 638 compiler.reportError(node, | |
| 639 new CompileTimeConstantError(kind, const [])); | |
| 640 } else { | |
| 641 lazyStatics.add(element); | |
| 642 return null; | |
| 643 } | |
| 644 } | |
| 645 pendingVariables.add(element); | |
| 646 | |
| 647 SendSet assignment = node.asSendSet(); | |
| 648 Constant value; | |
| 649 if (assignment === null) { | |
| 650 // No initial value. | |
| 651 value = new NullConstant(); | |
| 652 } else { | |
| 653 Node right = assignment.arguments.head; | |
| 654 value = | |
| 655 compileNodeWithDefinitions(right, definitions, isConst: isConst); | |
| 656 } | |
| 657 if (value != null) { | |
| 658 initialVariableValues[element] = value; | |
| 659 } else { | |
| 660 assert(!isConst); | |
| 661 lazyStatics.add(element); | |
| 662 } | |
| 663 pendingVariables.remove(element); | |
| 664 return value; | |
| 665 }); | |
| 666 } | |
| 667 | |
| 668 Constant compileNodeWithDefinitions(Node node, | |
| 669 TreeElements definitions, | |
| 670 [bool isConst]) { | |
| 671 return measure(() { | |
| 672 assert(node !== null); | |
| 673 CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator( | |
| 674 constantSystem, definitions, compiler, isConst); | |
| 675 return evaluator.evaluate(node); | |
| 676 }); | |
| 677 } | |
| 678 | |
| 679 /** Attempts to compile a constant expression. Returns null if not possible */ | |
| 680 Constant tryCompileNodeWithDefinitions(Node node, TreeElements definitions) { | |
| 681 return measure(() { | |
| 682 assert(node !== null); | |
| 683 try { | |
| 684 TryCompileTimeConstantEvaluator evaluator = | |
| 685 new TryCompileTimeConstantEvaluator(constantSystem, | |
| 686 definitions, | |
| 687 compiler); | |
| 688 return evaluator.evaluate(node); | |
| 689 } on CompileTimeConstantError catch (exn) { | |
| 690 return null; | |
| 691 } | |
| 692 }); | |
| 693 } | |
| 694 | |
| 695 /** | |
| 696 * Returns a [List] of static non final fields that need to be initialized. | |
| 697 * The list must be evaluated in order since the fields might depend on each | |
| 698 * other. | |
| 699 */ | |
| 700 List<VariableElement> getStaticNonFinalFieldsForEmission() { | |
| 701 return initialVariableValues.getKeys().filter((element) { | |
| 702 return element.kind == ElementKind.FIELD | |
| 703 && !element.isInstanceMember() | |
| 704 && !element.modifiers.isFinal(); | |
| 705 }); | |
| 706 } | |
| 707 | |
| 708 /** | |
| 709 * Returns a [List] of static const fields that need to be initialized. The | |
| 710 * list must be evaluated in order since the fields might depend on each | |
| 711 * other. | |
| 712 */ | |
| 713 List<VariableElement> getStaticFinalFieldsForEmission() { | |
| 714 return initialVariableValues.getKeys().filter((element) { | |
| 715 return element.kind == ElementKind.FIELD | |
| 716 && !element.isInstanceMember() | |
| 717 && element.modifiers.isFinal(); | |
| 718 }); | |
| 719 } | |
| 720 | |
| 721 List<VariableElement> getLazilyInitializedFieldsForEmission() { | |
| 722 return new List<VariableElement>.from(lazyStatics); | |
| 723 } | |
| 724 | |
| 725 List<Constant> getConstantsForEmission() { | |
| 726 // We must emit dependencies before their uses. | |
| 727 Set<Constant> seenConstants = new Set<Constant>(); | |
| 728 List<Constant> result = new List<Constant>(); | |
| 729 | |
| 730 void addConstant(Constant constant) { | |
| 731 if (!seenConstants.contains(constant)) { | |
| 732 constant.getDependencies().forEach(addConstant); | |
| 733 assert(!seenConstants.contains(constant)); | |
| 734 result.add(constant); | |
| 735 seenConstants.add(constant); | |
| 736 } | |
| 737 } | |
| 738 | |
| 739 compiledConstants.forEach((Constant key, ignored) => addConstant(key)); | |
| 740 return result; | |
| 741 } | |
| 742 | |
| 743 String getNameForConstant(Constant constant) { | |
| 744 return compiledConstants[constant]; | |
| 745 } | |
| 746 | |
| 747 /** This function writes the constant in non-canonicalized form. */ | |
| 748 CodeBuffer writeJsCode(CodeBuffer buffer, Constant value) { | |
| 749 value._writeJsCode(buffer, this); | |
| 750 return buffer; | |
| 751 } | |
| 752 | |
| 753 CodeBuffer writeConstant(CodeBuffer buffer, Constant value) { | |
| 754 value._writeCanonicalizedJsCode(buffer, this); | |
| 755 return buffer; | |
| 756 } | |
| 757 | |
| 758 CodeBuffer writeJsCodeForVariable(CodeBuffer buffer, | |
| 759 VariableElement element) { | |
| 760 if (!initialVariableValues.containsKey(element)) { | |
| 761 compiler.internalError("No initial value for given element", | |
| 762 element: element); | |
| 763 } | |
| 764 Constant constant = initialVariableValues[element]; | |
| 765 writeConstant(buffer, constant); | |
| 766 return buffer; | |
| 767 } | |
| 768 | |
| 769 /** | |
| 770 * Write the contents of the quoted string to a [CodeBuffer] in | |
| 771 * a form that is valid as JavaScript string literal content. | |
| 772 * The string is assumed quoted by single quote characters. | |
| 773 */ | |
| 774 static void writeEscapedString(DartString string, | |
| 775 CodeBuffer buffer, | |
| 776 void cancel(String reason)) { | |
| 777 Iterator<int> iterator = string.iterator(); | |
| 778 while (iterator.hasNext()) { | |
| 779 int code = iterator.next(); | |
| 780 if (code === $SQ) { | |
| 781 buffer.add(@"\'"); | |
| 782 } else if (code === $LF) { | |
| 783 buffer.add(@'\n'); | |
| 784 } else if (code === $CR) { | |
| 785 buffer.add(@'\r'); | |
| 786 } else if (code === $LS) { | |
| 787 // This Unicode line terminator and $PS are invalid in JS string | |
| 788 // literals. | |
| 789 buffer.add(@'\u2028'); | |
| 790 } else if (code === $PS) { | |
| 791 buffer.add(@'\u2029'); | |
| 792 } else if (code === $BACKSLASH) { | |
| 793 buffer.add(@'\\'); | |
| 794 } else { | |
| 795 if (code > 0xffff) { | |
| 796 cancel('Unhandled non-BMP character: U+${code.toRadixString(16)}'); | |
| 797 } | |
| 798 // TODO(lrn): Consider whether all codes above 0x7f really need to | |
| 799 // be escaped. We build a Dart string here, so it should be a literal | |
| 800 // stage that converts it to, e.g., UTF-8 for a JS interpreter. | |
| 801 if (code < 0x20) { | |
| 802 buffer.add(@'\x'); | |
| 803 if (code < 0x10) buffer.add('0'); | |
| 804 buffer.add(code.toRadixString(16)); | |
| 805 } else if (code >= 0x80) { | |
| 806 if (code < 0x100) { | |
| 807 buffer.add(@'\x'); | |
| 808 } else { | |
| 809 buffer.add(@'\u'); | |
| 810 if (code < 0x1000) { | |
| 811 buffer.add('0'); | |
| 812 } | |
| 813 } | |
| 814 buffer.add(code.toRadixString(16)); | |
| 815 } else { | |
| 816 buffer.addCharCode(code); | |
| 817 } | |
| 818 } | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 String getJsConstructor(ClassElement element) { | |
| 823 return compiler.namer.isolatePropertiesAccess(element); | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 class CompileTimeConstantEvaluator extends AbstractVisitor { | |
| 828 bool isEvaluatingConstant; | |
| 829 final ConstantSystem constantSystem; | |
| 830 final TreeElements elements; | |
| 831 final Compiler compiler; | |
| 832 | |
| 833 CompileTimeConstantEvaluator(this.constantSystem, | |
| 834 this.elements, | |
| 835 this.compiler, | |
| 836 [bool isConst]) | |
| 837 : this.isEvaluatingConstant = isConst; | |
| 838 | |
| 839 Constant evaluate(Node node) { | |
| 840 return node.accept(this); | |
| 841 } | |
| 842 | |
| 843 Constant evaluateConstant(Node node) { | |
| 844 bool oldIsEvaluatingConstant = isEvaluatingConstant; | |
| 845 isEvaluatingConstant = true; | |
| 846 Constant result = node.accept(this); | |
| 847 isEvaluatingConstant = oldIsEvaluatingConstant; | |
| 848 assert(result != null); | |
| 849 return result; | |
| 850 } | |
| 851 | |
| 852 Constant visitNode(Node node) { | |
| 853 return signalNotCompileTimeConstant(node); | |
| 854 } | |
| 855 | |
| 856 Constant visitLiteralBool(LiteralBool node) { | |
| 857 return constantSystem.createBool(node.value); | |
| 858 } | |
| 859 | |
| 860 Constant visitLiteralDouble(LiteralDouble node) { | |
| 861 return constantSystem.createDouble(node.value); | |
| 862 } | |
| 863 | |
| 864 Constant visitLiteralInt(LiteralInt node) { | |
| 865 return constantSystem.createInt(node.value); | |
| 866 } | |
| 867 | |
| 868 Constant visitLiteralList(LiteralList node) { | |
| 869 if (!node.isConst()) { | |
| 870 return signalNotCompileTimeConstant(node); | |
| 871 } | |
| 872 List<Constant> arguments = <Constant>[]; | |
| 873 for (Link<Node> link = node.elements.nodes; | |
| 874 !link.isEmpty(); | |
| 875 link = link.tail) { | |
| 876 arguments.add(evaluateConstant(link.head)); | |
| 877 } | |
| 878 // TODO(floitsch): get type from somewhere. | |
| 879 DartType type = null; | |
| 880 Constant constant = new ListConstant(type, arguments); | |
| 881 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 882 return constant; | |
| 883 } | |
| 884 | |
| 885 Constant visitLiteralMap(LiteralMap node) { | |
| 886 if (!node.isConst()) { | |
| 887 signalNotCompileTimeConstant(node); | |
| 888 error(node); | |
| 889 } | |
| 890 List<StringConstant> keys = <StringConstant>[]; | |
| 891 Map<StringConstant, Constant> map = new Map<StringConstant, Constant>(); | |
| 892 for (Link<Node> link = node.entries.nodes; | |
| 893 !link.isEmpty(); | |
| 894 link = link.tail) { | |
| 895 LiteralMapEntry entry = link.head; | |
| 896 Constant key = evaluateConstant(entry.key); | |
| 897 if (!key.isString() || entry.key.asStringNode() === null) { | |
| 898 MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL; | |
| 899 compiler.reportError(entry.key, new ResolutionError(kind, const [])); | |
| 900 } | |
| 901 StringConstant keyConstant = key; | |
| 902 if (!map.containsKey(key)) keys.add(key); | |
| 903 map[key] = evaluateConstant(entry.value); | |
| 904 } | |
| 905 List<Constant> values = <Constant>[]; | |
| 906 Constant protoValue = null; | |
| 907 for (StringConstant key in keys) { | |
| 908 if (key.value == const LiteralDartString(MapConstant.PROTO_PROPERTY)) { | |
| 909 protoValue = map[key]; | |
| 910 } else { | |
| 911 values.add(map[key]); | |
| 912 } | |
| 913 } | |
| 914 bool hasProtoKey = (protoValue !== null); | |
| 915 // TODO(floitsch): this should be a List<String> type. | |
| 916 DartType keysType = null; | |
| 917 ListConstant keysList = new ListConstant(keysType, keys); | |
| 918 compiler.constantHandler.registerCompileTimeConstant(keysList); | |
| 919 SourceString className = hasProtoKey | |
| 920 ? MapConstant.DART_PROTO_CLASS | |
| 921 : MapConstant.DART_CLASS; | |
| 922 ClassElement classElement = compiler.jsHelperLibrary.find(className); | |
| 923 classElement.ensureResolved(compiler); | |
| 924 // TODO(floitsch): copy over the generic type. | |
| 925 DartType type = new InterfaceType(classElement); | |
| 926 compiler.enqueuer.codegen.registerInstantiatedClass(classElement); | |
| 927 Constant constant = new MapConstant(type, keysList, values, protoValue); | |
| 928 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 929 return constant; | |
| 930 } | |
| 931 | |
| 932 Constant visitLiteralNull(LiteralNull node) { | |
| 933 return constantSystem.createNull(); | |
| 934 } | |
| 935 | |
| 936 Constant visitLiteralString(LiteralString node) { | |
| 937 return constantSystem.createString(node.dartString, node); | |
| 938 } | |
| 939 | |
| 940 Constant visitStringJuxtaposition(StringJuxtaposition node) { | |
| 941 StringConstant left = evaluate(node.first); | |
| 942 StringConstant right = evaluate(node.second); | |
| 943 if (left == null || right == null) return null; | |
| 944 return constantSystem.createString( | |
| 945 new DartString.concat(left.value, right.value), node); | |
| 946 } | |
| 947 | |
| 948 Constant visitStringInterpolation(StringInterpolation node) { | |
| 949 StringConstant initialString = evaluate(node.string); | |
| 950 if (initialString == null) return null; | |
| 951 DartString accumulator = initialString.value; | |
| 952 for (StringInterpolationPart part in node.parts) { | |
| 953 Constant expression = evaluate(part.expression); | |
| 954 DartString expressionString; | |
| 955 if (expression.isNum() || expression.isBool()) { | |
| 956 PrimitiveConstant primitive = expression; | |
| 957 expressionString = new DartString.literal(primitive.value.toString()); | |
| 958 } else if (expression.isString()) { | |
| 959 PrimitiveConstant primitive = expression; | |
| 960 expressionString = primitive.value; | |
| 961 } else { | |
| 962 return signalNotCompileTimeConstant(part.expression); | |
| 963 } | |
| 964 accumulator = new DartString.concat(accumulator, expressionString); | |
| 965 StringConstant partString = evaluate(part.string); | |
| 966 if (partString == null) return null; | |
| 967 accumulator = new DartString.concat(accumulator, partString.value); | |
| 968 }; | |
| 969 return constantSystem.createString(accumulator, node); | |
| 970 } | |
| 971 | |
| 972 // TODO(floitsch): provide better error-messages. | |
| 973 Constant visitSend(Send send) { | |
| 974 Element element = elements[send]; | |
| 975 if (Elements.isStaticOrTopLevelField(element)) { | |
| 976 Constant result; | |
| 977 if (element.modifiers !== null) { | |
| 978 if (element.modifiers.isConst()) { | |
| 979 result = compiler.compileConstant(element); | |
| 980 } else if (element.modifiers.isFinal()) { | |
| 981 // TODO(4516): remove support for final compile-time constants: if | |
| 982 // isCompilingConstant is true don't compile the variable. | |
| 983 result = compiler.compileVariable(element); | |
| 984 } | |
| 985 } | |
| 986 if (result == null) return signalNotCompileTimeConstant(send); | |
| 987 return result; | |
| 988 } else if (Elements.isStaticOrTopLevelFunction(element) | |
| 989 && send.isPropertyAccess) { | |
| 990 compiler.codegenWorld.staticFunctionsNeedingGetter.add(element); | |
| 991 Constant constant = new FunctionConstant(element); | |
| 992 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 993 return constant; | |
| 994 } else if (send.isPrefix) { | |
| 995 assert(send.isOperator); | |
| 996 Constant receiverConstant = evaluate(send.receiver); | |
| 997 if (receiverConstant == null) return null; | |
| 998 Operator op = send.selector; | |
| 999 Constant folded; | |
| 1000 switch (op.source.stringValue) { | |
| 1001 case "!": | |
| 1002 folded = constantSystem.not.fold(receiverConstant); | |
| 1003 break; | |
| 1004 case "-": | |
| 1005 folded = constantSystem.negate.fold(receiverConstant); | |
| 1006 break; | |
| 1007 case "~": | |
| 1008 folded = constantSystem.bitNot.fold(receiverConstant); | |
| 1009 break; | |
| 1010 default: | |
| 1011 compiler.internalError("Unexpected operator.", node: op); | |
| 1012 break; | |
| 1013 } | |
| 1014 if (folded === null) return signalNotCompileTimeConstant(send); | |
| 1015 return folded; | |
| 1016 } else if (send.isOperator && !send.isPostfix) { | |
| 1017 assert(send.argumentCount() == 1); | |
| 1018 Constant left = evaluate(send.receiver); | |
| 1019 Constant right = evaluate(send.argumentsNode.nodes.head); | |
| 1020 if (left == null || right == null) return null; | |
| 1021 Operator op = send.selector.asOperator(); | |
| 1022 Constant folded = null; | |
| 1023 switch (op.source.stringValue) { | |
| 1024 case "+": | |
| 1025 folded = constantSystem.add.fold(left, right); | |
| 1026 break; | |
| 1027 case "-": | |
| 1028 folded = constantSystem.subtract.fold(left, right); | |
| 1029 break; | |
| 1030 case "*": | |
| 1031 folded = constantSystem.multiply.fold(left, right); | |
| 1032 break; | |
| 1033 case "/": | |
| 1034 folded = constantSystem.divide.fold(left, right); | |
| 1035 break; | |
| 1036 case "%": | |
| 1037 folded = constantSystem.modulo.fold(left, right); | |
| 1038 break; | |
| 1039 case "~/": | |
| 1040 folded = constantSystem.truncatingDivide.fold(left, right); | |
| 1041 break; | |
| 1042 case "|": | |
| 1043 folded = constantSystem.bitOr.fold(left, right); | |
| 1044 break; | |
| 1045 case "&": | |
| 1046 folded = constantSystem.bitAnd.fold(left, right); | |
| 1047 break; | |
| 1048 case "^": | |
| 1049 folded = constantSystem.bitXor.fold(left, right); | |
| 1050 break; | |
| 1051 case "||": | |
| 1052 folded = constantSystem.booleanOr.fold(left, right); | |
| 1053 break; | |
| 1054 case "&&": | |
| 1055 folded = constantSystem.booleanAnd.fold(left, right); | |
| 1056 break; | |
| 1057 case "<<": | |
| 1058 folded = constantSystem.shiftLeft.fold(left, right); | |
| 1059 break; | |
| 1060 case ">>": | |
| 1061 folded = constantSystem.shiftRight.fold(left, right); | |
| 1062 break; | |
| 1063 case "<": | |
| 1064 folded = constantSystem.less.fold(left, right); | |
| 1065 break; | |
| 1066 case "<=": | |
| 1067 folded = constantSystem.lessEqual.fold(left, right); | |
| 1068 break; | |
| 1069 case ">": | |
| 1070 folded = constantSystem.greater.fold(left, right); | |
| 1071 break; | |
| 1072 case ">=": | |
| 1073 folded = constantSystem.greaterEqual.fold(left, right); | |
| 1074 break; | |
| 1075 case "==": | |
| 1076 if (left.isPrimitive() && right.isPrimitive()) { | |
| 1077 folded = constantSystem.equal.fold(left, right); | |
| 1078 } | |
| 1079 break; | |
| 1080 case "===": | |
| 1081 if (left.isPrimitive() && right.isPrimitive()) { | |
| 1082 folded = constantSystem.identity.fold(left, right); | |
| 1083 } | |
| 1084 break; | |
| 1085 case "!=": | |
| 1086 if (left.isPrimitive() && right.isPrimitive()) { | |
| 1087 BoolConstant areEquals = constantSystem.equal.fold(left, right); | |
| 1088 if (areEquals === null) { | |
| 1089 folded = null; | |
| 1090 } else { | |
| 1091 folded = areEquals.negate(); | |
| 1092 } | |
| 1093 } | |
| 1094 break; | |
| 1095 case "!==": | |
| 1096 if (left.isPrimitive() && right.isPrimitive()) { | |
| 1097 BoolConstant areIdentical = | |
| 1098 constantSystem.identity.fold(left, right); | |
| 1099 if (areIdentical === null) { | |
| 1100 folded = null; | |
| 1101 } else { | |
| 1102 folded = areIdentical.negate(); | |
| 1103 } | |
| 1104 } | |
| 1105 break; | |
| 1106 } | |
| 1107 if (folded === null) return signalNotCompileTimeConstant(send); | |
| 1108 return folded; | |
| 1109 } | |
| 1110 return signalNotCompileTimeConstant(send); | |
| 1111 } | |
| 1112 | |
| 1113 Constant visitSendSet(SendSet node) { | |
| 1114 return signalNotCompileTimeConstant(node); | |
| 1115 } | |
| 1116 | |
| 1117 /** Returns the list of constants that are passed to the static function. */ | |
| 1118 List<Constant> evaluateArgumentsToConstructor(Selector selector, | |
| 1119 Link<Node> arguments, | |
| 1120 FunctionElement target) { | |
| 1121 List<Constant> compiledArguments = <Constant>[]; | |
| 1122 | |
| 1123 Function compileArgument = evaluateConstant; | |
| 1124 Function compileConstant = compiler.compileConstant; | |
| 1125 bool succeeded = selector.addArgumentsToList(arguments, | |
| 1126 compiledArguments, | |
| 1127 target, | |
| 1128 compileArgument, | |
| 1129 compileConstant, | |
| 1130 compiler); | |
| 1131 assert(succeeded); | |
| 1132 return compiledArguments; | |
| 1133 } | |
| 1134 | |
| 1135 Constant visitNewExpression(NewExpression node) { | |
| 1136 if (!node.isConst()) { | |
| 1137 return signalNotCompileTimeConstant(node); | |
| 1138 } | |
| 1139 | |
| 1140 Send send = node.send; | |
| 1141 FunctionElement constructor = elements[send]; | |
| 1142 ClassElement classElement = constructor.getEnclosingClass(); | |
| 1143 if (classElement.isInterface()) { | |
| 1144 compiler.resolver.resolveMethodElement(constructor); | |
| 1145 constructor = constructor.defaultImplementation; | |
| 1146 classElement = constructor.getEnclosingClass(); | |
| 1147 } | |
| 1148 | |
| 1149 Selector selector = elements.getSelector(send); | |
| 1150 List<Constant> arguments = | |
| 1151 evaluateArgumentsToConstructor(selector, send.arguments, constructor); | |
| 1152 ConstructorEvaluator evaluator = | |
| 1153 new ConstructorEvaluator(constructor, constantSystem, compiler); | |
| 1154 evaluator.evaluateConstructorFieldValues(arguments); | |
| 1155 List<Constant> jsNewArguments = evaluator.buildJsNewArguments(classElement); | |
| 1156 | |
| 1157 compiler.enqueuer.codegen.registerInstantiatedClass(classElement); | |
| 1158 // TODO(floitsch): take generic types into account. | |
| 1159 DartType type = classElement.computeType(compiler); | |
| 1160 Constant constant = new ConstructedConstant(type, jsNewArguments); | |
| 1161 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 1162 return constant; | |
| 1163 } | |
| 1164 | |
| 1165 Constant visitParenthesizedExpression(ParenthesizedExpression node) { | |
| 1166 return node.expression.accept(this); | |
| 1167 } | |
| 1168 | |
| 1169 error(Node node) { | |
| 1170 // TODO(floitsch): get the list of constants that are currently compiled | |
| 1171 // and present some kind of stack-trace. | |
| 1172 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; | |
| 1173 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); | |
| 1174 } | |
| 1175 | |
| 1176 Constant signalNotCompileTimeConstant(Node node) { | |
| 1177 if (isEvaluatingConstant) { | |
| 1178 error(node); | |
| 1179 } | |
| 1180 // Else we don't need to do anything. The final handler is only | |
| 1181 // optimistically trying to compile constants. So it is normal that we | |
| 1182 // sometimes see non-compile time constants. | |
| 1183 // Simply return [:null:] which is used to propagate a failing | |
| 1184 // compile-time compilation. | |
| 1185 return null; | |
| 1186 } | |
| 1187 } | |
| 1188 | |
| 1189 class TryCompileTimeConstantEvaluator extends CompileTimeConstantEvaluator { | |
| 1190 TryCompileTimeConstantEvaluator(ConstantSystem constantSystem, | |
| 1191 TreeElements elements, | |
| 1192 Compiler compiler) | |
| 1193 : super(constantSystem, elements, compiler, isConst: true); | |
| 1194 | |
| 1195 error(Node node) { | |
| 1196 // Just fail without reporting it anywhere. | |
| 1197 throw new CompileTimeConstantError( | |
| 1198 MessageKind.NOT_A_COMPILE_TIME_CONSTANT, const []); | |
| 1199 } | |
| 1200 } | |
| 1201 | |
| 1202 class ConstructorEvaluator extends CompileTimeConstantEvaluator { | |
| 1203 FunctionElement constructor; | |
| 1204 final Map<Element, Constant> definitions; | |
| 1205 final Map<Element, Constant> fieldValues; | |
| 1206 | |
| 1207 ConstructorEvaluator(FunctionElement constructor, | |
| 1208 ConstantSystem constantSystem, | |
| 1209 Compiler compiler) | |
| 1210 : this.constructor = constructor, | |
| 1211 this.definitions = new Map<Element, Constant>(), | |
| 1212 this.fieldValues = new Map<Element, Constant>(), | |
| 1213 super(constantSystem, | |
| 1214 compiler.resolver.resolveMethodElement(constructor), | |
| 1215 compiler, | |
| 1216 isConst: true); | |
| 1217 | |
| 1218 Constant visitSend(Send send) { | |
| 1219 Element element = elements[send]; | |
| 1220 if (Elements.isLocal(element)) { | |
| 1221 Constant constant = definitions[element]; | |
| 1222 if (constant === null) { | |
| 1223 compiler.internalError("Local variable without value", node: send); | |
| 1224 } | |
| 1225 return constant; | |
| 1226 } | |
| 1227 return super.visitSend(send); | |
| 1228 } | |
| 1229 | |
| 1230 /** | |
| 1231 * Given the arguments (a list of constants) assigns them to the parameters, | |
| 1232 * updating the definitions map. If the constructor has field-initializer | |
| 1233 * parameters (like [:this.x:]), also updates the [fieldValues] map. | |
| 1234 */ | |
| 1235 void assignArgumentsToParameters(List<Constant> arguments) { | |
| 1236 // Assign arguments to parameters. | |
| 1237 FunctionSignature parameters = constructor.computeSignature(compiler); | |
| 1238 int index = 0; | |
| 1239 parameters.forEachParameter((Element parameter) { | |
| 1240 Constant argument = arguments[index++]; | |
| 1241 definitions[parameter] = argument; | |
| 1242 if (parameter.kind == ElementKind.FIELD_PARAMETER) { | |
| 1243 FieldParameterElement fieldParameterElement = parameter; | |
| 1244 fieldValues[fieldParameterElement.fieldElement] = argument; | |
| 1245 } | |
| 1246 }); | |
| 1247 } | |
| 1248 | |
| 1249 void evaluateSuperOrRedirectSend(Selector selector, | |
| 1250 Link<Node> arguments, | |
| 1251 FunctionElement targetConstructor) { | |
| 1252 List<Constant> compiledArguments = | |
| 1253 evaluateArgumentsToConstructor(selector, arguments, targetConstructor); | |
| 1254 | |
| 1255 ConstructorEvaluator evaluator = new ConstructorEvaluator( | |
| 1256 targetConstructor, constantSystem, compiler); | |
| 1257 evaluator.evaluateConstructorFieldValues(compiledArguments); | |
| 1258 // Copy over the fieldValues from the super/redirect-constructor. | |
| 1259 evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value); | |
| 1260 } | |
| 1261 | |
| 1262 /** | |
| 1263 * Runs through the initializers of the given [constructor] and updates | |
| 1264 * the [fieldValues] map. | |
| 1265 */ | |
| 1266 void evaluateConstructorInitializers() { | |
| 1267 FunctionExpression functionNode = constructor.parseNode(compiler); | |
| 1268 NodeList initializerList = functionNode.initializers; | |
| 1269 | |
| 1270 bool foundSuperOrRedirect = false; | |
| 1271 | |
| 1272 if (initializerList !== null) { | |
| 1273 for (Link<Node> link = initializerList.nodes; | |
| 1274 !link.isEmpty(); | |
| 1275 link = link.tail) { | |
| 1276 assert(link.head is Send); | |
| 1277 if (link.head is !SendSet) { | |
| 1278 // A super initializer or constructor redirection. | |
| 1279 Send call = link.head; | |
| 1280 FunctionElement targetConstructor = elements[call]; | |
| 1281 Selector selector = elements.getSelector(call); | |
| 1282 Link<Node> arguments = call.arguments; | |
| 1283 evaluateSuperOrRedirectSend(selector, arguments, targetConstructor); | |
| 1284 foundSuperOrRedirect = true; | |
| 1285 } else { | |
| 1286 // A field initializer. | |
| 1287 SendSet init = link.head; | |
| 1288 Link<Node> initArguments = init.arguments; | |
| 1289 assert(!initArguments.isEmpty() && initArguments.tail.isEmpty()); | |
| 1290 Constant fieldValue = evaluate(initArguments.head); | |
| 1291 fieldValues[elements[init]] = fieldValue; | |
| 1292 } | |
| 1293 } | |
| 1294 } | |
| 1295 | |
| 1296 if (!foundSuperOrRedirect) { | |
| 1297 // No super initializer found. Try to find the default constructor if | |
| 1298 // the class is not Object. | |
| 1299 ClassElement enclosingClass = constructor.getEnclosingClass(); | |
| 1300 ClassElement superClass = enclosingClass.superclass; | |
| 1301 if (enclosingClass != compiler.objectClass) { | |
| 1302 assert(superClass !== null); | |
| 1303 assert(superClass.resolutionState == STATE_DONE); | |
| 1304 FunctionElement targetConstructor = | |
| 1305 superClass.lookupConstructor(superClass.name); | |
| 1306 if (targetConstructor === null) { | |
| 1307 compiler.internalError("no default constructor available", | |
| 1308 node: functionNode); | |
| 1309 } | |
| 1310 | |
| 1311 Selector selector = new Selector.call(superClass.name, | |
| 1312 enclosingClass.getLibrary(), | |
| 1313 0); | |
| 1314 evaluateSuperOrRedirectSend(selector, | |
| 1315 const EmptyLink<Node>(), | |
| 1316 targetConstructor); | |
| 1317 } | |
| 1318 } | |
| 1319 } | |
| 1320 | |
| 1321 /** | |
| 1322 * Simulates the execution of the [constructor] with the given | |
| 1323 * [arguments] to obtain the field values that need to be passed to the | |
| 1324 * native JavaScript constructor. | |
| 1325 */ | |
| 1326 void evaluateConstructorFieldValues(List<Constant> arguments) { | |
| 1327 compiler.withCurrentElement(constructor, () { | |
| 1328 assignArgumentsToParameters(arguments); | |
| 1329 evaluateConstructorInitializers(); | |
| 1330 }); | |
| 1331 } | |
| 1332 | |
| 1333 List<Constant> buildJsNewArguments(ClassElement classElement) { | |
| 1334 List<Constant> jsNewArguments = <Constant>[]; | |
| 1335 classElement.forEachInstanceField( | |
| 1336 includeBackendMembers: true, | |
| 1337 includeSuperMembers: true, | |
| 1338 f: (ClassElement enclosing, Element field) { | |
| 1339 Constant fieldValue = fieldValues[field]; | |
| 1340 if (fieldValue === null) { | |
| 1341 // Use the default value. | |
| 1342 fieldValue = compiler.compileConstant(field); | |
| 1343 } | |
| 1344 jsNewArguments.add(fieldValue); | |
| 1345 }); | |
| 1346 return jsNewArguments; | |
| 1347 } | |
| 1348 } | |
| OLD | NEW |