| 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 |