Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: lib/compiler/implementation/constants.dart

Issue 10917097: Move constants into their own file. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase (on top of copy of compile_time_constants.dart) Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/compile_time_constants.dart ('k') | lib/compiler/implementation/leg.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698