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 library dart2js.compile_time_constant_evaluator; | 5 library dart2js.compile_time_constant_evaluator; |
6 | 6 |
7 import 'constant_system_dart.dart'; | 7 import 'constant_system_dart.dart'; |
8 import 'constants/constant_system.dart'; | 8 import 'constants/constant_system.dart'; |
9 import 'constants/expressions.dart'; | 9 import 'constants/expressions.dart'; |
10 import 'constants/values.dart'; | 10 import 'constants/values.dart'; |
11 import 'dart_types.dart'; | 11 import 'dart_types.dart'; |
12 import 'dart2jslib.dart' show Compiler, CompilerTask, MessageKind, invariant; | 12 import 'dart2jslib.dart' show Compiler, CompilerTask, MessageKind, invariant; |
13 import 'elements/elements.dart'; | 13 import 'elements/elements.dart'; |
14 import 'elements/modelx.dart' show FunctionElementX; | 14 import 'elements/modelx.dart' show FunctionElementX; |
| 15 import 'helpers/helpers.dart'; |
15 import 'resolution/resolution.dart'; | 16 import 'resolution/resolution.dart'; |
16 import 'resolution/operators.dart'; | 17 import 'resolution/operators.dart'; |
17 import 'tree/tree.dart'; | 18 import 'tree/tree.dart'; |
18 import 'util/util.dart' show Link; | 19 import 'util/util.dart' show Link; |
19 import 'universe/universe.dart' show CallStructure; | 20 import 'universe/universe.dart' show CallStructure; |
20 | 21 |
21 /// A [ConstantEnvironment] provides access for constants compiled for variable | 22 /// A [ConstantEnvironment] provides access for constants compiled for variable |
22 /// initializers. | 23 /// initializers. |
23 abstract class ConstantEnvironment { | 24 abstract class ConstantEnvironment { |
24 /// Returns the constant for the initializer of [element]. | 25 /// Returns the constant for the initializer of [element]. |
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 } | 455 } |
455 | 456 |
456 AstConstant visitLiteralSymbol(LiteralSymbol node) { | 457 AstConstant visitLiteralSymbol(LiteralSymbol node) { |
457 InterfaceType type = compiler.symbolClass.rawType; | 458 InterfaceType type = compiler.symbolClass.rawType; |
458 String text = node.slowNameString; | 459 String text = node.slowNameString; |
459 List<AstConstant> arguments = | 460 List<AstConstant> arguments = |
460 <AstConstant>[new AstConstant(context, node, | 461 <AstConstant>[new AstConstant(context, node, |
461 new StringConstantExpression( | 462 new StringConstantExpression( |
462 text, | 463 text, |
463 constantSystem.createString(new LiteralDartString(text))))]; | 464 constantSystem.createString(new LiteralDartString(text))))]; |
464 AstConstant constant = makeConstructedConstant( | 465 ConstructorElement constructor = compiler.symbolConstructor; |
465 compiler, handler, context, node, type, compiler.symbolConstructor, | 466 AstConstant constant = createConstructorInvocation( |
466 CallStructure.ONE_ARG, | 467 node, type, constructor, CallStructure.ONE_ARG, |
467 arguments, arguments); | 468 normalizedArguments: arguments); |
468 return new AstConstant( | 469 return new AstConstant( |
469 context, node, new SymbolConstantExpression(constant.value, text)); | 470 context, node, new SymbolConstantExpression(constant.value, text)); |
470 } | 471 } |
471 | 472 |
472 AstConstant makeTypeConstant(Node node, DartType elementType) { | 473 AstConstant makeTypeConstant(Node node, DartType elementType) { |
473 DartType constantType = | 474 DartType constantType = |
474 compiler.backend.typeImplementation.computeType(compiler); | 475 compiler.backend.typeImplementation.computeType(compiler); |
475 return new AstConstant( | 476 return new AstConstant( |
476 context, node, new TypeConstantExpression( | 477 context, node, new TypeConstantExpression( |
477 new TypeConstantValue(elementType, constantType), | 478 new TypeConstantValue(elementType, constantType), |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
706 compileArgument, | 707 compileArgument, |
707 compileDefaultValue); | 708 compileDefaultValue); |
708 } | 709 } |
709 | 710 |
710 AstConstant visitNewExpression(NewExpression node) { | 711 AstConstant visitNewExpression(NewExpression node) { |
711 if (!node.isConst) { | 712 if (!node.isConst) { |
712 return signalNotCompileTimeConstant(node); | 713 return signalNotCompileTimeConstant(node); |
713 } | 714 } |
714 | 715 |
715 Send send = node.send; | 716 Send send = node.send; |
716 FunctionElement constructor = elements[send]; | 717 ConstructorElement constructor = elements[send]; |
717 if (Elements.isUnresolved(constructor)) { | 718 if (Elements.isUnresolved(constructor)) { |
718 return signalNotCompileTimeConstant(node); | 719 return signalNotCompileTimeConstant(node); |
719 } | 720 } |
720 | 721 |
721 // Deferred types can not be used in const instance creation expressions. | 722 // Deferred types can not be used in const instance creation expressions. |
722 // Check if the constructor comes from a deferred library. | 723 // Check if the constructor comes from a deferred library. |
723 if (isDeferredUse(node.send.selector.asSend())) { | 724 if (isDeferredUse(node.send.selector.asSend())) { |
724 return signalNotCompileTimeConstant(node, | 725 return signalNotCompileTimeConstant(node, |
725 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION); | 726 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION); |
726 } | 727 } |
727 | 728 |
| 729 InterfaceType type = elements.getType(node); |
| 730 CallStructure callStructure = elements.getSelector(send).callStructure; |
| 731 |
| 732 return createConstructorInvocation( |
| 733 node, type, constructor, callStructure, |
| 734 arguments: node.send.arguments); |
| 735 } |
| 736 |
| 737 AstConstant createConstructorInvocation( |
| 738 Node node, |
| 739 InterfaceType type, |
| 740 ConstructorElement constructor, |
| 741 CallStructure callStructure, |
| 742 {Link<Node> arguments, |
| 743 List<AstConstant> normalizedArguments}) { |
728 // TODO(ahe): This is nasty: we must eagerly analyze the | 744 // TODO(ahe): This is nasty: we must eagerly analyze the |
729 // constructor to ensure the redirectionTarget has been computed | 745 // constructor to ensure the redirectionTarget has been computed |
730 // correctly. Find a way to avoid this. | 746 // correctly. Find a way to avoid this. |
731 compiler.analyzeElement(constructor.declaration); | 747 compiler.analyzeElement(constructor.declaration); |
732 | 748 |
733 InterfaceType type = elements.getType(node); | 749 // The redirection chain of this element may not have been resolved through |
734 CallStructure callStructure = elements.getSelector(send).callStructure; | 750 // a post-process action, so we have to make sure it is done here. |
| 751 compiler.resolver.resolveRedirectionChain(constructor, node); |
| 752 InterfaceType constructedType = |
| 753 constructor.computeEffectiveTargetType(type); |
| 754 ConstructorElement target = constructor.effectiveTarget; |
| 755 // The constructor must be an implementation to ensure that field |
| 756 // initializers are handled correctly. |
| 757 ConstructorElement implementation = target.implementation; |
735 | 758 |
736 Map<Node, AstConstant> concreteArgumentMap = | 759 if (implementation.isErroneous) { |
737 <Node, AstConstant>{}; | 760 return new AstConstant( |
738 for (Link<Node> link = send.arguments; !link.isEmpty; link = link.tail) { | 761 context, node, new ConstructedConstantExpression( |
739 Node argument = link.head; | 762 new ConstructedConstantValue( |
740 NamedArgument namedArgument = argument.asNamedArgument(); | 763 constructedType, const <FieldElement, ConstantValue>{}), |
741 if (namedArgument != null) { | 764 type, |
742 argument = namedArgument.expression; | 765 constructor, |
743 } | 766 callStructure, |
744 concreteArgumentMap[argument] = evaluateConstant(argument); | 767 const <ConstantExpression>[])); |
745 } | 768 } |
746 | 769 |
747 List<AstConstant> normalizedArguments = | 770 List<AstConstant> concreteArguments; |
748 evaluateArgumentsToConstructor( | 771 if (arguments != null) { |
749 node, callStructure, send.arguments, constructor.implementation, | 772 Map<Node, AstConstant> concreteArgumentMap = |
750 compileArgument: (node) => concreteArgumentMap[node]); | 773 <Node, AstConstant>{}; |
751 List<AstConstant> concreteArguments = | 774 for (Link<Node> link = arguments; !link.isEmpty; link = link.tail) { |
752 concreteArgumentMap.values.toList(); | 775 Node argument = link.head; |
753 | 776 NamedArgument namedArgument = argument.asNamedArgument(); |
754 if (constructor == compiler.intEnvironment || | 777 if (namedArgument != null) { |
755 constructor == compiler.boolEnvironment || | 778 argument = namedArgument.expression; |
756 constructor == compiler.stringEnvironment) { | 779 } |
757 | 780 concreteArgumentMap[argument] = evaluateConstant(argument); |
758 AstConstant createEvaluatedConstant(ConstantValue value) { | |
759 return new AstConstant( | |
760 context, node, new ConstructedConstantExpression( | |
761 value, | |
762 type, | |
763 constructor, | |
764 elements.getSelector(send).callStructure, | |
765 concreteArguments.map((e) => e.expression).toList())); | |
766 } | 781 } |
767 | 782 |
768 var firstArgument = normalizedArguments[0].value; | 783 normalizedArguments = evaluateArgumentsToConstructor( |
769 ConstantValue defaultValue = normalizedArguments[1].value; | 784 node, callStructure, arguments, implementation, |
| 785 compileArgument: (node) => concreteArgumentMap[node]); |
| 786 concreteArguments = concreteArgumentMap.values.toList(); |
| 787 } else { |
| 788 assert(normalizedArguments != null); |
| 789 concreteArguments = normalizedArguments; |
| 790 } |
770 | 791 |
771 if (firstArgument.isNull) { | 792 if (target == compiler.intEnvironment || |
772 compiler.reportError( | 793 target == compiler.boolEnvironment || |
773 send.arguments.head, MessageKind.NULL_NOT_ALLOWED); | 794 target == compiler.stringEnvironment) { |
774 return null; | 795 return createFromEnvironmentConstant( |
775 } | 796 node, constructedType, target, |
776 | 797 callStructure, normalizedArguments, concreteArguments); |
777 if (!firstArgument.isString) { | |
778 DartType type = defaultValue.getType(compiler.coreTypes); | |
779 compiler.reportError( | |
780 send.arguments.head, MessageKind.NOT_ASSIGNABLE, | |
781 {'fromType': type, 'toType': compiler.stringClass.rawType}); | |
782 return null; | |
783 } | |
784 | |
785 if (constructor == compiler.intEnvironment && | |
786 !(defaultValue.isNull || defaultValue.isInt)) { | |
787 DartType type = defaultValue.getType(compiler.coreTypes); | |
788 compiler.reportError( | |
789 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, | |
790 {'fromType': type, 'toType': compiler.intClass.rawType}); | |
791 return null; | |
792 } | |
793 | |
794 if (constructor == compiler.boolEnvironment && | |
795 !(defaultValue.isNull || defaultValue.isBool)) { | |
796 DartType type = defaultValue.getType(compiler.coreTypes); | |
797 compiler.reportError( | |
798 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, | |
799 {'fromType': type, 'toType': compiler.boolClass.rawType}); | |
800 return null; | |
801 } | |
802 | |
803 if (constructor == compiler.stringEnvironment && | |
804 !(defaultValue.isNull || defaultValue.isString)) { | |
805 DartType type = defaultValue.getType(compiler.coreTypes); | |
806 compiler.reportError( | |
807 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, | |
808 {'fromType': type, 'toType': compiler.stringClass.rawType}); | |
809 return null; | |
810 } | |
811 | |
812 String value = | |
813 compiler.fromEnvironment(firstArgument.primitiveValue.slowToString()); | |
814 | |
815 if (value == null) { | |
816 return createEvaluatedConstant(defaultValue); | |
817 } else if (constructor == compiler.intEnvironment) { | |
818 int number = int.parse(value, onError: (_) => null); | |
819 return createEvaluatedConstant( | |
820 (number == null) | |
821 ? defaultValue | |
822 : constantSystem.createInt(number)); | |
823 } else if (constructor == compiler.boolEnvironment) { | |
824 if (value == 'true') { | |
825 return createEvaluatedConstant(constantSystem.createBool(true)); | |
826 } else if (value == 'false') { | |
827 return createEvaluatedConstant(constantSystem.createBool(false)); | |
828 } else { | |
829 return createEvaluatedConstant(defaultValue); | |
830 } | |
831 } else { | |
832 assert(constructor == compiler.stringEnvironment); | |
833 return createEvaluatedConstant( | |
834 constantSystem.createString(new DartString.literal(value))); | |
835 } | |
836 } else { | 798 } else { |
837 return makeConstructedConstant( | 799 return makeConstructedConstant( |
838 compiler, handler, context, | 800 compiler, handler, context, node, |
839 node, type, constructor, callStructure, | 801 type, constructor, |
840 concreteArguments, normalizedArguments); | 802 constructedType, implementation, |
| 803 callStructure, concreteArguments, normalizedArguments); |
841 } | 804 } |
842 } | 805 } |
843 | 806 |
| 807 AstConstant createFromEnvironmentConstant( |
| 808 Node node, |
| 809 InterfaceType type, |
| 810 ConstructorElement constructor, |
| 811 CallStructure callStructure, |
| 812 List<AstConstant> normalizedArguments, |
| 813 List<AstConstant> concreteArguments) { |
| 814 AstConstant createEvaluatedConstant(ConstantValue value) { |
| 815 return new AstConstant( |
| 816 context, node, new ConstructedConstantExpression( |
| 817 value, |
| 818 type, |
| 819 constructor, |
| 820 callStructure, |
| 821 concreteArguments.map((e) => e.expression).toList())); |
| 822 } |
| 823 |
| 824 var firstArgument = normalizedArguments[0].value; |
| 825 ConstantValue defaultValue = normalizedArguments[1].value; |
| 826 |
| 827 if (firstArgument.isNull) { |
| 828 compiler.reportError( |
| 829 normalizedArguments[0].node, MessageKind.NULL_NOT_ALLOWED); |
| 830 return null; |
| 831 } |
| 832 |
| 833 if (!firstArgument.isString) { |
| 834 DartType type = defaultValue.getType(compiler.coreTypes); |
| 835 compiler.reportError( |
| 836 normalizedArguments[0].node, MessageKind.NOT_ASSIGNABLE, |
| 837 {'fromType': type, 'toType': compiler.stringClass.rawType}); |
| 838 return null; |
| 839 } |
| 840 |
| 841 if (constructor == compiler.intEnvironment && |
| 842 !(defaultValue.isNull || defaultValue.isInt)) { |
| 843 DartType type = defaultValue.getType(compiler.coreTypes); |
| 844 compiler.reportError( |
| 845 normalizedArguments[1].node, MessageKind.NOT_ASSIGNABLE, |
| 846 {'fromType': type, 'toType': compiler.intClass.rawType}); |
| 847 return null; |
| 848 } |
| 849 |
| 850 if (constructor == compiler.boolEnvironment && |
| 851 !(defaultValue.isNull || defaultValue.isBool)) { |
| 852 DartType type = defaultValue.getType(compiler.coreTypes); |
| 853 compiler.reportError( |
| 854 normalizedArguments[1].node, MessageKind.NOT_ASSIGNABLE, |
| 855 {'fromType': type, 'toType': compiler.boolClass.rawType}); |
| 856 return null; |
| 857 } |
| 858 |
| 859 if (constructor == compiler.stringEnvironment && |
| 860 !(defaultValue.isNull || defaultValue.isString)) { |
| 861 DartType type = defaultValue.getType(compiler.coreTypes); |
| 862 compiler.reportError( |
| 863 normalizedArguments[1].node, MessageKind.NOT_ASSIGNABLE, |
| 864 {'fromType': type, 'toType': compiler.stringClass.rawType}); |
| 865 return null; |
| 866 } |
| 867 |
| 868 String value = |
| 869 compiler.fromEnvironment(firstArgument.primitiveValue.slowToString()); |
| 870 |
| 871 if (value == null) { |
| 872 return createEvaluatedConstant(defaultValue); |
| 873 } else if (constructor == compiler.intEnvironment) { |
| 874 int number = int.parse(value, onError: (_) => null); |
| 875 return createEvaluatedConstant( |
| 876 (number == null) |
| 877 ? defaultValue |
| 878 : constantSystem.createInt(number)); |
| 879 } else if (constructor == compiler.boolEnvironment) { |
| 880 if (value == 'true') { |
| 881 return createEvaluatedConstant(constantSystem.createBool(true)); |
| 882 } else if (value == 'false') { |
| 883 return createEvaluatedConstant(constantSystem.createBool(false)); |
| 884 } else { |
| 885 return createEvaluatedConstant(defaultValue); |
| 886 } |
| 887 } else { |
| 888 assert(constructor == compiler.stringEnvironment); |
| 889 return createEvaluatedConstant( |
| 890 constantSystem.createString(new DartString.literal(value))); |
| 891 } |
| 892 } |
| 893 |
844 static AstConstant makeConstructedConstant( | 894 static AstConstant makeConstructedConstant( |
845 Compiler compiler, | 895 Compiler compiler, |
846 ConstantCompilerBase handler, | 896 ConstantCompilerBase handler, |
847 Element context, | 897 Element context, |
848 Node node, | 898 Node node, |
849 InterfaceType type, | 899 InterfaceType type, |
850 ConstructorElement constructor, | 900 ConstructorElement constructor, |
| 901 InterfaceType constructedType, |
| 902 ConstructorElement target, |
851 CallStructure callStructure, | 903 CallStructure callStructure, |
852 List<AstConstant> concreteArguments, | 904 List<AstConstant> concreteArguments, |
853 List<AstConstant> normalizedArguments) { | 905 List<AstConstant> normalizedArguments) { |
| 906 assert(invariant(node, !target.isRedirectingFactory, |
| 907 message: "makeConstructedConstant can only be called with the " |
| 908 "effective target: $constructor")); |
854 assert(invariant(node, callStructure.signatureApplies(constructor) || | 909 assert(invariant(node, callStructure.signatureApplies(constructor) || |
855 compiler.compilationFailed, | 910 compiler.compilationFailed, |
856 message: "Call structure $callStructure does not apply to constructor " | 911 message: "Call structure $callStructure does not apply to constructor " |
857 "$constructor.")); | 912 "$constructor.")); |
858 | 913 |
859 // The redirection chain of this element may not have been resolved through | |
860 // a post-process action, so we have to make sure it is done here. | |
861 compiler.resolver.resolveRedirectionChain(constructor, node); | |
862 InterfaceType constructedType = | |
863 constructor.computeEffectiveTargetType(type); | |
864 ConstructorElement target = constructor.effectiveTarget; | |
865 ClassElement classElement = target.enclosingClass; | |
866 // The constructor must be an implementation to ensure that field | |
867 // initializers are handled correctly. | |
868 target = target.implementation; | |
869 assert(invariant(node, target.isImplementation)); | |
870 | |
871 ConstructorEvaluator evaluator = new ConstructorEvaluator( | 914 ConstructorEvaluator evaluator = new ConstructorEvaluator( |
872 constructedType, target, handler, compiler); | 915 constructedType, target, handler, compiler); |
873 evaluator.evaluateConstructorFieldValues(normalizedArguments); | 916 evaluator.evaluateConstructorFieldValues(normalizedArguments); |
874 Map<FieldElement, AstConstant> fieldConstants = | 917 Map<FieldElement, AstConstant> fieldConstants = |
875 evaluator.buildFieldConstants(classElement); | 918 evaluator.buildFieldConstants(target.enclosingClass); |
876 Map<FieldElement, ConstantValue> fieldValues = | 919 Map<FieldElement, ConstantValue> fieldValues = |
877 <FieldElement, ConstantValue>{}; | 920 <FieldElement, ConstantValue>{}; |
878 fieldConstants.forEach((FieldElement field, AstConstant astConstant) { | 921 fieldConstants.forEach((FieldElement field, AstConstant astConstant) { |
879 fieldValues[field] = astConstant.value; | 922 fieldValues[field] = astConstant.value; |
880 }); | 923 }); |
881 return new AstConstant( | 924 return new AstConstant( |
882 context, node, new ConstructedConstantExpression( | 925 context, node, new ConstructedConstantExpression( |
883 new ConstructedConstantValue(constructedType, fieldValues), | 926 new ConstructedConstantValue(constructedType, fieldValues), |
884 type, | 927 type, |
885 constructor, | 928 constructor, |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1147 ConstantValue get value => expression.value; | 1190 ConstantValue get value => expression.value; |
1148 | 1191 |
1149 String toString() => expression.toString(); | 1192 String toString() => expression.toString(); |
1150 } | 1193 } |
1151 | 1194 |
1152 /// A synthetic constant used to recover from errors. | 1195 /// A synthetic constant used to recover from errors. |
1153 class ErroneousAstConstant extends AstConstant { | 1196 class ErroneousAstConstant extends AstConstant { |
1154 ErroneousAstConstant(Element element, Node node) | 1197 ErroneousAstConstant(Element element, Node node) |
1155 : super(element, node, new ErroneousConstantExpression()); | 1198 : super(element, node, new ErroneousConstantExpression()); |
1156 } | 1199 } |
OLD | NEW |