Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/compile_time_constants.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart |
| index e717c15d48684de33dc8b8c17e651c1046ac53b9..ef4a8631e5565efccb91f9305598a1601a5d9f08 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart |
| @@ -8,7 +8,7 @@ part of dart2js; |
| /// initializers. |
| abstract class ConstantEnvironment { |
| /// Returns the constant for the initializer of [element]. |
| - Constant getConstantForVariable(VariableElement element); |
| + ConstExp getConstantForVariable(VariableElement element); |
| } |
| /// A class that can compile and provide constants for variables, nodes and |
| @@ -21,7 +21,7 @@ abstract class ConstantCompiler extends ConstantEnvironment { |
| /// the compile-time constant for the backend interpretation of constants. |
| /// |
| /// The returned constant is always of the frontend interpretation. |
| - Constant compileConstant(VariableElement element); |
| + ConstExp compileConstant(VariableElement element); |
| /// Computes the compile-time constant for the variable initializer, |
| /// if possible. |
| @@ -34,7 +34,7 @@ abstract class ConstantCompiler extends ConstantEnvironment { |
| /// the compile-time constant for the backend interpretation of constants. |
| /// |
| /// The returned constant is always of the frontend interpretation. |
| - Constant compileNode(Node node, TreeElements elements); |
| + ConstExp compileNode(Node node, TreeElements elements); |
| /// Compiles the compile-time constant for the value [metadata], or reports an |
| /// error if the value is not a compile-time constant. |
| @@ -43,7 +43,7 @@ abstract class ConstantCompiler extends ConstantEnvironment { |
| /// the compile-time constant for the backend interpretation of constants. |
| /// |
| /// The returned constant is always of the frontend interpretation. |
| - Constant compileMetadata(MetadataAnnotation metadata, |
| + ConstExp compileMetadata(MetadataAnnotation metadata, |
| Node node, TreeElements elements); |
| } |
| @@ -53,10 +53,10 @@ abstract class BackendConstantEnvironment extends ConstantEnvironment { |
| /// Returns the compile-time constant associated with [node]. |
| /// |
| /// Depending on implementation, the constant might be stored in [elements]. |
| - Constant getConstantForNode(Node node, TreeElements elements); |
| + ConstExp getConstantForNode(Node node, TreeElements elements); |
| /// Returns the compile-time constant value of [metadata]. |
| - Constant getConstantForMetadata(MetadataAnnotation metadata); |
| + ConstExp getConstantForMetadata(MetadataAnnotation metadata); |
| } |
| /// Interface for the task that compiles the constant environments for the |
| @@ -83,32 +83,32 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| * |
| * Invariant: The keys in this map are declarations. |
| */ |
| - final Map<VariableElement, Constant> initialVariableValues = |
| - new Map<VariableElement, Constant>(); |
| + final Map<VariableElement, ConstExp> initialVariableValues = |
| + new Map<VariableElement, ConstExp>(); |
| /** The set of variable elements that are in the process of being computed. */ |
| final Set<VariableElement> pendingVariables = new Set<VariableElement>(); |
| ConstantCompilerBase(this.compiler, this.constantSystem); |
| - Constant getConstantForVariable(VariableElement element) { |
| + ConstExp getConstantForVariable(VariableElement element) { |
| return initialVariableValues[element.declaration]; |
| } |
| - Constant compileConstant(VariableElement element) { |
| + ConstExp compileConstant(VariableElement element) { |
| return compileVariable(element, isConst: true); |
| } |
| - Constant compileVariable(VariableElement element, {bool isConst: false}) { |
| + ConstExp compileVariable(VariableElement element, {bool isConst: false}) { |
| if (initialVariableValues.containsKey(element.declaration)) { |
| - Constant result = initialVariableValues[element.declaration]; |
| + ConstExp result = initialVariableValues[element.declaration]; |
| return result; |
| } |
| AstElement currentElement = element.analyzableElement; |
| return compiler.withCurrentElement(currentElement, () { |
| compiler.analyzeElement(currentElement.declaration); |
| - Constant constant = compileVariableWithDefinitions( |
| + ConstExp constant = compileVariableWithDefinitions( |
| element, currentElement.resolvedAst.elements, isConst: isConst); |
| return constant; |
| }); |
| @@ -120,7 +120,7 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| * If the variable is `const` but cannot be compiled eagerly reports an |
| * error. |
| */ |
| - Constant compileVariableWithDefinitions(VariableElement element, |
| + ConstExp compileVariableWithDefinitions(VariableElement element, |
| TreeElements definitions, |
| {bool isConst: false}) { |
| Node node = element.node; |
| @@ -134,10 +134,10 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| pendingVariables.add(element); |
| Expression initializer = element.initializer; |
| - Constant value; |
| + ConstExp value; |
| if (initializer == null) { |
| // No initial value. |
| - value = new NullConstant(); |
| + value = new PrimitiveConstExp(new NullConstant()); |
| } else { |
| value = compileNodeWithDefinitions( |
| initializer, definitions, isConst: isConst); |
| @@ -145,7 +145,7 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| value != null && |
| element.isField) { |
| DartType elementType = element.type; |
| - if (elementType.isMalformed && !value.isNull) { |
| + if (elementType.isMalformed && !value.value.isNull) { |
| if (isConst) { |
| ErroneousElement element = elementType.element; |
| compiler.reportFatalError( |
| @@ -155,7 +155,7 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| value = null; |
| } |
| } else { |
| - DartType constantType = value.computeType(compiler); |
| + DartType constantType = value.value.computeType(compiler); |
| if (!constantSystem.isSubtype(compiler, |
| constantType, elementType)) { |
| if (isConst) { |
| @@ -174,26 +174,28 @@ abstract class ConstantCompilerBase implements ConstantCompiler { |
| if (value != null) { |
| initialVariableValues[element.declaration] = value; |
| } else { |
| - assert(!isConst); |
| + assert(invariant(element, !isConst, |
| + message: "Variable $element does not compile to a constant.")); |
| } |
| pendingVariables.remove(element); |
| return value; |
| } |
| - Constant compileNodeWithDefinitions(Node node, |
| + ConstExp compileNodeWithDefinitions(Node node, |
| TreeElements definitions, |
| {bool isConst: true}) { |
| assert(node != null); |
| CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator( |
| this, definitions, compiler, isConst: isConst); |
| - return evaluator.evaluate(node); |
| + EvaluatedConstant constant = evaluator.evaluate(node); |
| + return constant != null ? constant.expression : null; |
| } |
| - Constant compileNode(Node node, TreeElements elements) { |
| + ConstExp compileNode(Node node, TreeElements elements) { |
| return compileNodeWithDefinitions(node, elements); |
| } |
| - Constant compileMetadata(MetadataAnnotation metadata, |
| + ConstExp compileMetadata(MetadataAnnotation metadata, |
| Node node, |
| TreeElements elements) { |
| return compileNodeWithDefinitions(node, elements); |
| @@ -206,18 +208,18 @@ class DartConstantCompiler extends ConstantCompilerBase { |
| DartConstantCompiler(Compiler compiler) |
| : super(compiler, const DartConstantSystem()); |
| - Constant getConstantForNode(Node node, TreeElements definitions) { |
| + ConstExp getConstantForNode(Node node, TreeElements definitions) { |
| return definitions.getConstant(node); |
| } |
| - Constant getConstantForMetadata(MetadataAnnotation metadata) { |
| - return metadata.value; |
| + ConstExp getConstantForMetadata(MetadataAnnotation metadata) { |
| + return metadata.constant; |
| } |
| - Constant compileNodeWithDefinitions(Node node, |
| + ConstExp compileNodeWithDefinitions(Node node, |
| TreeElements definitions, |
| {bool isConst: true}) { |
| - Constant constant = definitions.getConstant(node); |
| + ConstExp constant = definitions.getConstant(node); |
| if (constant != null) { |
| return constant; |
| } |
| @@ -231,12 +233,14 @@ class DartConstantCompiler extends ConstantCompilerBase { |
| } |
| // TODO(johnniwinther): Change to create [ConstExp] instead of [Constant]. |
|
sigurdm
2014/09/17 10:29:40
I think this TODO is solved now?
Johnni Winther
2014/09/17 12:20:39
Done.
|
| -class CompileTimeConstantEvaluator extends Visitor { |
| +class CompileTimeConstantEvaluator extends Visitor<EvaluatedConstant> { |
|
sigurdm
2014/09/17 10:29:40
We might want to consider evaluating the constants
Johnni Winther
2014/09/17 12:20:39
Added a TODO.
|
| bool isEvaluatingConstant; |
| final ConstantCompilerBase handler; |
| final TreeElements elements; |
| final Compiler compiler; |
| + Element get context => elements.analyzedElement; |
| + |
| CompileTimeConstantEvaluator(this.handler, |
| this.elements, |
| this.compiler, |
| @@ -245,129 +249,191 @@ class CompileTimeConstantEvaluator extends Visitor { |
| ConstantSystem get constantSystem => handler.constantSystem; |
| - Constant evaluate(Node node) { |
| + EvaluatedConstant evaluate(Node node) { |
| return node.accept(this); |
| } |
| - Constant evaluateConstant(Node node) { |
| + EvaluatedConstant evaluateConstant(Node node) { |
| bool oldIsEvaluatingConstant = isEvaluatingConstant; |
| isEvaluatingConstant = true; |
| - Constant result = node.accept(this); |
| + EvaluatedConstant result = node.accept(this); |
| isEvaluatingConstant = oldIsEvaluatingConstant; |
| assert(result != null); |
| return result; |
| } |
| - Constant visitNode(Node node) { |
| + EvaluatedConstant visitNode(Node node) { |
| return signalNotCompileTimeConstant(node); |
| } |
| - Constant visitLiteralBool(LiteralBool node) { |
| - return constantSystem.createBool(node.value); |
| + EvaluatedConstant visitLiteralBool(LiteralBool node) { |
| + return new EvaluatedConstant( |
| + context, node, new PrimitiveConstExp( |
| + constantSystem.createBool(node.value))); |
| } |
| - Constant visitLiteralDouble(LiteralDouble node) { |
| - return constantSystem.createDouble(node.value); |
| + EvaluatedConstant visitLiteralDouble(LiteralDouble node) { |
| + return new EvaluatedConstant( |
| + context, node, new PrimitiveConstExp( |
| + constantSystem.createDouble(node.value))); |
| } |
| - Constant visitLiteralInt(LiteralInt node) { |
| - return constantSystem.createInt(node.value); |
| + EvaluatedConstant visitLiteralInt(LiteralInt node) { |
| + return new EvaluatedConstant( |
| + context, node, new PrimitiveConstExp( |
| + constantSystem.createInt(node.value))); |
| } |
| - Constant visitLiteralList(LiteralList node) { |
| + EvaluatedConstant visitLiteralList(LiteralList node) { |
| if (!node.isConst) { |
| return signalNotCompileTimeConstant(node); |
| } |
| - List<Constant> arguments = <Constant>[]; |
| + List<ConstExp> argumentExpressions = <ConstExp>[]; |
| + List<Constant> argumentValues = <Constant>[]; |
| for (Link<Node> link = node.elements.nodes; |
| !link.isEmpty; |
| link = link.tail) { |
| - arguments.add(evaluateConstant(link.head)); |
| + EvaluatedConstant argument = evaluateConstant(link.head); |
| + if (argument == null) { |
| + return null; |
| + } |
| + argumentExpressions.add(argument.expression); |
| + argumentValues.add(argument.value); |
| } |
| DartType type = elements.getType(node); |
| - return new ListConstant(type, arguments); |
| + return new EvaluatedConstant( |
| + context, node, new ListConstExp( |
| + new ListConstant(type, argumentValues), |
| + type, |
| + argumentExpressions)); |
| } |
| - Constant visitLiteralMap(LiteralMap node) { |
| + EvaluatedConstant visitLiteralMap(LiteralMap node) { |
| if (!node.isConst) { |
| return signalNotCompileTimeConstant(node); |
| } |
| - List<Constant> keys = <Constant>[]; |
| - Map<Constant, Constant> map = new Map<Constant, Constant>(); |
| + List<ConstExp> keyExpressions = <ConstExp>[]; |
| + List<Constant> keyValues = <Constant>[]; |
| + Map<Constant, ConstExp> map = new Map<Constant, ConstExp>(); |
| for (Link<Node> link = node.entries.nodes; |
| !link.isEmpty; |
| link = link.tail) { |
| LiteralMapEntry entry = link.head; |
| - Constant key = evaluateConstant(entry.key); |
| - if (!map.containsKey(key)) { |
| - keys.add(key); |
| + EvaluatedConstant key = evaluateConstant(entry.key); |
| + if (key == null) { |
| + return null; |
| + } |
| + if (!map.containsKey(key.value)) { |
| + keyExpressions.add(key.expression); |
| + keyValues.add(key.value); |
| } else { |
| compiler.reportWarning(entry.key, MessageKind.EQUAL_MAP_ENTRY_KEY); |
| } |
| - map[key] = evaluateConstant(entry.value); |
| + EvaluatedConstant value = evaluateConstant(entry.value); |
| + if (value == null) { |
| + return null; |
| + } |
| + map[key.value] = value.expression; |
| } |
| - List<Constant> values = map.values.toList(); |
| - InterfaceType sourceType = elements.getType(node); |
| - return constantSystem.createMap(compiler, sourceType, keys, values); |
| + List<ConstExp> valueExpressions = map.values.toList(); |
| + InterfaceType type = elements.getType(node); |
| + return new EvaluatedConstant( |
| + context, node, new MapConstExp( |
| + constantSystem.createMap(compiler, type, keyValues, |
| + valueExpressions.map((e) => e.value).toList()), |
| + type, |
| + keyExpressions, |
| + valueExpressions)); |
| } |
| - Constant visitLiteralNull(LiteralNull node) { |
| - return constantSystem.createNull(); |
| + EvaluatedConstant visitLiteralNull(LiteralNull node) { |
| + return new EvaluatedConstant( |
| + context, node, new PrimitiveConstExp( |
| + constantSystem.createNull())); |
| } |
| - Constant visitLiteralString(LiteralString node) { |
| - return constantSystem.createString(node.dartString); |
| + EvaluatedConstant visitLiteralString(LiteralString node) { |
| + return new EvaluatedConstant( |
| + context, node, new PrimitiveConstExp( |
| + constantSystem.createString(node.dartString))); |
| } |
| - Constant visitStringJuxtaposition(StringJuxtaposition node) { |
| - StringConstant left = evaluate(node.first); |
| - StringConstant right = evaluate(node.second); |
| + EvaluatedConstant visitStringJuxtaposition(StringJuxtaposition node) { |
| + EvaluatedConstant left = evaluate(node.first); |
| + EvaluatedConstant right = evaluate(node.second); |
| if (left == null || right == null) return null; |
| - return constantSystem.createString( |
| - new DartString.concat(left.value, right.value)); |
| + StringConstant leftValue = left.value; |
| + StringConstant rightValue = right.value; |
| + return new EvaluatedConstant( |
| + context, node, new ConcatenateConstExp( |
| + constantSystem.createString( |
| + new DartString.concat(leftValue.value, rightValue.value)), |
| + [left.expression, right.expression])); |
| } |
| - Constant visitStringInterpolation(StringInterpolation node) { |
| - StringConstant initialString = evaluate(node.string); |
| - if (initialString == null) return null; |
| - DartString accumulator = initialString.value; |
| + EvaluatedConstant visitStringInterpolation(StringInterpolation node) { |
| + List<ConstExp> subexpressions = <ConstExp>[]; |
| + EvaluatedConstant initialString = evaluate(node.string); |
| + if (initialString == null) { |
| + return null; |
| + } |
| + subexpressions.add(initialString.expression); |
| + StringConstant initialStringValue = initialString.value; |
| + DartString accumulator = initialStringValue.value; |
| for (StringInterpolationPart part in node.parts) { |
| - Constant expression = evaluate(part.expression); |
| + EvaluatedConstant subexpression = evaluate(part.expression); |
| + if (subexpression == null) { |
| + return null; |
| + } |
| + subexpressions.add(subexpression.expression); |
| + Constant expression = subexpression.value; |
| DartString expressionString; |
| - if (expression == null) { |
| - return signalNotCompileTimeConstant(part.expression); |
| - } else if (expression.isNum || expression.isBool) { |
| + if (expression.isNum || expression.isBool) { |
| PrimitiveConstant primitive = expression; |
| expressionString = new DartString.literal(primitive.value.toString()); |
| } else if (expression.isString) { |
| PrimitiveConstant primitive = expression; |
| expressionString = primitive.value; |
| } else { |
| + // TODO(johnniwinther): Specialize message to indicated that the problem |
| + // is not constness but the types of the const expressions. |
| return signalNotCompileTimeConstant(part.expression); |
| } |
| accumulator = new DartString.concat(accumulator, expressionString); |
| - StringConstant partString = evaluate(part.string); |
| + EvaluatedConstant partString = evaluate(part.string); |
| if (partString == null) return null; |
| - accumulator = new DartString.concat(accumulator, partString.value); |
| + subexpressions.add(partString.expression); |
| + StringConstant partStringValue = partString.value; |
| + accumulator = new DartString.concat(accumulator, partStringValue.value); |
| }; |
| - return constantSystem.createString(accumulator); |
| + return new EvaluatedConstant( |
| + context, node, new ConcatenateConstExp( |
| + constantSystem.createString(accumulator), |
| + subexpressions)); |
| } |
| - Constant visitLiteralSymbol(LiteralSymbol node) { |
| + EvaluatedConstant visitLiteralSymbol(LiteralSymbol node) { |
| InterfaceType type = compiler.symbolClass.rawType; |
| - List<Constant> createArguments(_) { |
| - return [constantSystem.createString( |
| - new DartString.literal(node.slowNameString))]; |
| - } |
| - return makeConstructedConstant( |
| - compiler, handler, node, type, compiler.symbolConstructor, |
| - createArguments, isLiteralSymbol: true); |
| + String text = node.slowNameString; |
| + List<EvaluatedConstant> arguments = |
| + <EvaluatedConstant>[new EvaluatedConstant(context, node, |
| + new PrimitiveConstExp(constantSystem.createString( |
| + new DartString.literal(text))))]; |
| + EvaluatedConstant constant = makeConstructedConstant( |
| + compiler, handler, context, node, type, compiler.symbolConstructor, |
| + new Selector.callConstructor('', null, 1), |
| + arguments, arguments); |
| + return new EvaluatedConstant( |
| + context, node, new SymbolConstExp(constant.value, text)); |
| } |
| - Constant makeTypeConstant(DartType elementType) { |
| + EvaluatedConstant makeTypeConstant(Node node, DartType elementType) { |
| DartType constantType = |
| compiler.backend.typeImplementation.computeType(compiler); |
| - return new TypeConstant(elementType, constantType); |
| + return new EvaluatedConstant( |
| + context, node, new TypeConstExp( |
| + new TypeConstant(elementType, constantType), |
| + elementType)); |
| } |
| /// Returns true if the prefix of the send resolves to a deferred import |
| @@ -378,17 +444,18 @@ class CompileTimeConstantEvaluator extends Visitor { |
| .deferredPrefixElement(send, elements) != null; |
| } |
| - Constant visitIdentifier(Identifier node) { |
| + EvaluatedConstant visitIdentifier(Identifier node) { |
| Element element = elements[node]; |
| if (Elements.isClass(element) || Elements.isTypedef(element)) { |
| TypeDeclarationElement typeDeclarationElement = element; |
| - return makeTypeConstant(typeDeclarationElement.rawType); |
| + DartType type = typeDeclarationElement.rawType; |
| + return makeTypeConstant(node, type); |
| } |
| return signalNotCompileTimeConstant(node); |
| } |
| // TODO(floitsch): provide better error-messages. |
| - Constant visitSend(Send send) { |
| + EvaluatedConstant visitSend(Send send) { |
| Element element = elements[send]; |
| if (send.isPropertyAccess) { |
| if (isDeferredUse(send)) { |
| @@ -396,64 +463,91 @@ class CompileTimeConstantEvaluator extends Visitor { |
| message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT); |
| } |
| if (Elements.isStaticOrTopLevelFunction(element)) { |
| - return new FunctionConstant(element); |
| + return new EvaluatedConstant( |
| + context, send, new FunctionConstExp( |
| + new FunctionConstant(element), |
| + element)); |
| } else if (Elements.isStaticOrTopLevelField(element)) { |
| - Constant result; |
| + ConstExp result; |
| if (element.isConst) { |
| result = handler.compileConstant(element); |
| } else if (element.isFinal && !isEvaluatingConstant) { |
| result = handler.compileVariable(element); |
| } |
| - if (result != null) return result; |
| + if (result != null) { |
| + return new EvaluatedConstant( |
| + context, send, new VariableConstExp(result.value, element)); |
| + } |
| } else if (Elements.isClass(element) || Elements.isTypedef(element)) { |
| assert(elements.isTypeLiteral(send)); |
| - return makeTypeConstant(elements.getTypeLiteralType(send)); |
| + return makeTypeConstant(send, elements.getTypeLiteralType(send)); |
| } else if (send.receiver != null) { |
| // Fall through to error handling. |
| } else if (!Elements.isUnresolved(element) |
| && element.isVariable |
| && element.isConst) { |
| - Constant result = handler.compileConstant(element); |
| - if (result != null) return result; |
| + ConstExp result = handler.compileConstant(element); |
| + if (result != null) { |
| + return new EvaluatedConstant( |
| + context, send, new VariableConstExp(result.value, element)); |
| + } |
| } |
| return signalNotCompileTimeConstant(send); |
| } else if (send.isCall) { |
| if (identical(element, compiler.identicalFunction) |
| && send.argumentCount() == 2) { |
| - Constant left = evaluate(send.argumentsNode.nodes.head); |
| - Constant right = evaluate(send.argumentsNode.nodes.tail.head); |
| - Constant result = constantSystem.identity.fold(left, right); |
| - if (result != null) return result; |
| + EvaluatedConstant left = evaluate(send.argumentsNode.nodes.head); |
| + EvaluatedConstant right = evaluate(send.argumentsNode.nodes.tail.head); |
| + if (left == null || right == null) { |
| + return null; |
| + } |
| + Constant result = constantSystem.identity.fold(left.value, right.value); |
| + if (result != null) { |
| + return new EvaluatedConstant( |
| + context, send, new BinaryConstExp(result, |
| + left.expression, 'identical', right.expression)); |
| + } |
| } |
| return signalNotCompileTimeConstant(send); |
| } else if (send.isPrefix) { |
| assert(send.isOperator); |
| - Constant receiverConstant = evaluate(send.receiver); |
| - if (receiverConstant == null) return null; |
| + EvaluatedConstant receiverConstant = evaluate(send.receiver); |
| + if (receiverConstant == null) { |
| + return null; |
| + } |
| Operator op = send.selector; |
| UnaryOperation operation = constantSystem.lookupUnary(op.source); |
| if (operation == null) { |
| compiler.internalError(op, "Unexpected operator."); |
| } |
| - Constant folded = operation.fold(receiverConstant); |
| - if (folded == null) return signalNotCompileTimeConstant(send); |
| - return folded; |
| + Constant folded = operation.fold(receiverConstant.value); |
| + if (folded == null) { |
| + return signalNotCompileTimeConstant(send); |
| + } |
| + return new EvaluatedConstant( |
| + context, send, new UnaryConstExp(folded, |
| + op.source, receiverConstant.expression)); |
| } else if (send.isOperator && !send.isPostfix) { |
| assert(send.argumentCount() == 1); |
| - Constant left = evaluate(send.receiver); |
| - Constant right = evaluate(send.argumentsNode.nodes.head); |
| - if (left == null || right == null) return null; |
| + EvaluatedConstant left = evaluate(send.receiver); |
| + EvaluatedConstant right = evaluate(send.argumentsNode.nodes.head); |
| + if (left == null || right == null) { |
| + return null; |
| + } |
| + Constant leftValue = left.value; |
| + Constant rightValue = right.value; |
| Operator op = send.selector.asOperator(); |
| Constant folded = null; |
| switch (op.source) { |
| case "==": |
| - if (left.isPrimitive && right.isPrimitive) { |
| - folded = constantSystem.equal.fold(left, right); |
| + if (leftValue.isPrimitive && rightValue.isPrimitive) { |
| + folded = constantSystem.equal.fold(leftValue, rightValue); |
| } |
| break; |
| case "!=": |
| - if (left.isPrimitive && right.isPrimitive) { |
| - BoolConstant areEquals = constantSystem.equal.fold(left, right); |
| + if (leftValue.isPrimitive && rightValue.isPrimitive) { |
| + BoolConstant areEquals = |
| + constantSystem.equal.fold(leftValue, rightValue); |
| if (areEquals == null) { |
| folded = null; |
| } else { |
| @@ -464,21 +558,25 @@ class CompileTimeConstantEvaluator extends Visitor { |
| default: |
| BinaryOperation operation = constantSystem.lookupBinary(op.source); |
| if (operation != null) { |
| - folded = operation.fold(left, right); |
| + folded = operation.fold(leftValue, rightValue); |
| } |
| } |
| - if (folded == null) return signalNotCompileTimeConstant(send); |
| - return folded; |
| + if (folded == null) { |
| + return signalNotCompileTimeConstant(send); |
| + } |
| + return new EvaluatedConstant( |
| + context, send, new BinaryConstExp(folded, |
| + left.expression, op.source, right.expression)); |
| } |
| return signalNotCompileTimeConstant(send); |
| } |
| - Constant visitConditional(Conditional node) { |
| - Constant condition = evaluate(node.condition); |
| + EvaluatedConstant visitConditional(Conditional node) { |
| + EvaluatedConstant condition = evaluate(node.condition); |
| if (condition == null) { |
| return null; |
| - } else if (!condition.isBool) { |
| - DartType conditionType = condition.computeType(compiler); |
| + } else if (!condition.value.isBool) { |
| + DartType conditionType = condition.value.computeType(compiler); |
| if (isEvaluatingConstant) { |
| compiler.reportFatalError( |
| node.condition, MessageKind.NOT_ASSIGNABLE, |
| @@ -486,36 +584,50 @@ class CompileTimeConstantEvaluator extends Visitor { |
| } |
| return null; |
| } |
| - Constant thenExpression = evaluate(node.thenExpression); |
| - Constant elseExpression = evaluate(node.elseExpression); |
| - BoolConstant boolCondition = condition; |
| - return boolCondition.value ? thenExpression : elseExpression; |
| + EvaluatedConstant thenExpression = evaluate(node.thenExpression); |
| + EvaluatedConstant elseExpression = evaluate(node.elseExpression); |
| + if (thenExpression == null || elseExpression == null) { |
| + return null; |
| + } |
| + BoolConstant boolCondition = condition.value; |
| + return new EvaluatedConstant( |
| + context, node, new ConditionalConstExp( |
| + boolCondition.value ? thenExpression.value : elseExpression.value, |
| + condition.expression, |
| + thenExpression.expression, |
| + elseExpression.expression)); |
| } |
| - Constant visitSendSet(SendSet node) { |
| + EvaluatedConstant visitSendSet(SendSet node) { |
| return signalNotCompileTimeConstant(node); |
| } |
| /** |
| - * Returns the list of constants that are passed to the static function. |
| + * Returns the normalized list of constant arguments that are passed to the |
| + * constructor including both the concrete arguments and default values for |
| + * omitted optional arguments. |
| * |
| * Invariant: [target] must be an implementation element. |
| */ |
| - List<Constant> evaluateArgumentsToConstructor(Node node, |
| - Selector selector, |
| - Link<Node> arguments, |
| - FunctionElement target) { |
| + List<EvaluatedConstant> evaluateArgumentsToConstructor( |
| + Node node, |
| + Selector selector, |
| + Link<Node> arguments, |
| + FunctionElement target, |
| + {EvaluatedConstant compileArgument(Node node)}) { |
| assert(invariant(node, target.isImplementation)); |
| - List<Constant> compiledArguments = <Constant>[]; |
| + List<EvaluatedConstant> compiledArguments = <EvaluatedConstant>[]; |
| - Function compileArgument = evaluateConstant; |
| - Function compileConstant = handler.compileConstant; |
| + EvaluatedConstant compileDefaultValue(VariableElement element) { |
| + ConstExp constant = handler.compileConstant(element); |
| + return new EvaluatedConstant.fromDefaultValue(element, constant); |
| + } |
| target.computeSignature(compiler); |
| bool succeeded = selector.addArgumentsToList(arguments, |
| compiledArguments, |
| target, |
| compileArgument, |
| - compileConstant, |
| + compileDefaultValue, |
| compiler.world); |
| if (!succeeded) { |
| String name = Elements.constructorNameForDiagnostics( |
| @@ -528,7 +640,7 @@ class CompileTimeConstantEvaluator extends Visitor { |
| return compiledArguments; |
| } |
| - Constant visitNewExpression(NewExpression node) { |
| + EvaluatedConstant visitNewExpression(NewExpression node) { |
| if (!node.isConst) { |
| return signalNotCompileTimeConstant(node); |
| } |
| @@ -552,22 +664,47 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.analyzeElement(constructor.declaration); |
| InterfaceType type = elements.getType(node); |
| - List<Constant> evaluateArguments(FunctionElement constructor) { |
| - Selector selector = elements.getSelector(send); |
| - return evaluateArgumentsToConstructor( |
| - node, selector, send.arguments, constructor); |
| + Selector selector = elements.getSelector(send); |
| + |
| + Map<Node, EvaluatedConstant> concreteArgumentMap = |
| + <Node, EvaluatedConstant>{}; |
| + for (Link<Node> link = send.arguments; !link.isEmpty; link = link.tail) { |
| + Node argument = link.head; |
| + NamedArgument namedArgument = argument.asNamedArgument(); |
| + if (namedArgument != null) { |
| + argument = namedArgument.expression; |
| + } |
| + concreteArgumentMap[argument] = evaluateConstant(argument); |
| } |
| - if (constructor == compiler.intEnvironment |
| - || constructor == compiler.boolEnvironment |
| - || constructor == compiler.stringEnvironment) { |
| - List<Constant> arguments = evaluateArguments(constructor.implementation); |
| - var firstArgument = arguments[0]; |
| - Constant defaultValue = arguments[1]; |
| + List<EvaluatedConstant> normalizedArguments = |
| + evaluateArgumentsToConstructor( |
| + node, selector, send.arguments, constructor.implementation, |
| + compileArgument: (node) => concreteArgumentMap[node]); |
| + List<EvaluatedConstant> concreteArguments = |
| + concreteArgumentMap.values.toList(); |
| + |
| + if (constructor == compiler.intEnvironment || |
| + constructor == compiler.boolEnvironment || |
| + constructor == compiler.stringEnvironment) { |
| + |
| + EvaluatedConstant createEvaluatedConstant(Constant value) { |
| + return new EvaluatedConstant( |
| + context, node, new ConstructorConstExp( |
| + value, |
| + type, |
| + constructor, |
| + elements.getSelector(send), |
| + concreteArguments.map((e) => e.expression).toList())); |
| + } |
| + |
| + var firstArgument = normalizedArguments[0].value; |
| + Constant defaultValue = normalizedArguments[1].value; |
| if (firstArgument is NullConstant) { |
| compiler.reportFatalError( |
| send.arguments.head, MessageKind.NULL_NOT_ALLOWED); |
| + return null; |
| } |
| if (firstArgument is! StringConstant) { |
| @@ -575,6 +712,7 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.reportFatalError( |
| send.arguments.head, MessageKind.NOT_ASSIGNABLE, |
| {'fromType': type, 'toType': compiler.stringClass.rawType}); |
| + return null; |
| } |
| if (constructor == compiler.intEnvironment |
| @@ -583,6 +721,7 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.reportFatalError( |
| send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
| {'fromType': type, 'toType': compiler.intClass.rawType}); |
| + return null; |
| } |
| if (constructor == compiler.boolEnvironment |
| @@ -591,6 +730,7 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.reportFatalError( |
| send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
| {'fromType': type, 'toType': compiler.boolClass.rawType}); |
| + return null; |
| } |
| if (constructor == compiler.stringEnvironment |
| @@ -600,68 +740,85 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.reportFatalError( |
| send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
| {'fromType': type, 'toType': compiler.stringClass.rawType}); |
| + return null; |
| } |
| String value = |
| compiler.fromEnvironment(firstArgument.value.slowToString()); |
| if (value == null) { |
| - return defaultValue; |
| + return createEvaluatedConstant(defaultValue); |
| } else if (constructor == compiler.intEnvironment) { |
| int number = int.parse(value, onError: (_) => null); |
| - return (number == null) |
| - ? defaultValue |
| - : constantSystem.createInt(number); |
| + return createEvaluatedConstant( |
| + (number == null) |
| + ? defaultValue |
| + : constantSystem.createInt(number)); |
| } else if (constructor == compiler.boolEnvironment) { |
| if (value == 'true') { |
| - return constantSystem.createBool(true); |
| + return createEvaluatedConstant(constantSystem.createBool(true)); |
| } else if (value == 'false') { |
| - return constantSystem.createBool(false); |
| + return createEvaluatedConstant(constantSystem.createBool(false)); |
| } else { |
| - return defaultValue; |
| + return createEvaluatedConstant(defaultValue); |
| } |
| } else { |
| assert(constructor == compiler.stringEnvironment); |
| - return constantSystem.createString(new DartString.literal(value)); |
| + return createEvaluatedConstant( |
| + constantSystem.createString(new DartString.literal(value))); |
| } |
| } else { |
| return makeConstructedConstant( |
| - compiler, handler, node, type, constructor, evaluateArguments); |
| + compiler, handler, context, |
| + node, type, constructor, selector, |
| + concreteArguments, normalizedArguments); |
| } |
| } |
| - static Constant makeConstructedConstant( |
| + static EvaluatedConstant makeConstructedConstant( |
| Compiler compiler, |
| ConstantCompilerBase handler, |
| - Spannable node, |
| + Element context, |
| + Node node, |
| InterfaceType type, |
| ConstructorElement constructor, |
| - List<Constant> getArguments(ConstructorElement constructor), |
| - {bool isLiteralSymbol: false}) { |
| + Selector selector, |
| + List<EvaluatedConstant> concreteArguments, |
| + List<EvaluatedConstant> normalizedArguments) { |
| + assert(invariant(node, selector.applies(constructor, compiler.world), |
| + message: "Selector $selector does not apply to constructor " |
| + "$constructor.")); |
| // The redirection chain of this element may not have been resolved through |
| // a post-process action, so we have to make sure it is done here. |
| compiler.resolver.resolveRedirectionChain(constructor, node); |
| InterfaceType constructedType = |
| constructor.computeEffectiveTargetType(type); |
| - constructor = constructor.effectiveTarget; |
| - ClassElement classElement = constructor.enclosingClass; |
| + ConstructorElement target = constructor.effectiveTarget; |
| + ClassElement classElement = target.enclosingClass; |
| // The constructor must be an implementation to ensure that field |
| // initializers are handled correctly. |
| - constructor = constructor.implementation; |
| - assert(invariant(node, constructor.isImplementation)); |
| + target = target.implementation; |
| + assert(invariant(node, target.isImplementation)); |
| - List<Constant> arguments = getArguments(constructor); |
| ConstructorEvaluator evaluator = new ConstructorEvaluator( |
| - constructedType, constructor, handler, compiler); |
| - evaluator.evaluateConstructorFieldValues(arguments); |
| - List<Constant> jsNewArguments = evaluator.buildJsNewArguments(classElement); |
| - |
| - return new ConstructedConstant(constructedType, jsNewArguments, |
| - isLiteralSymbol: isLiteralSymbol); |
| + constructedType, target, handler, compiler); |
| + evaluator.evaluateConstructorFieldValues(normalizedArguments); |
| + List<EvaluatedConstant> fieldConstants = |
| + evaluator.buildFieldConstants(classElement); |
| + |
| + return new EvaluatedConstant( |
| + context, node, new ConstructorConstExp( |
| + new ConstructedConstant( |
| + constructedType, |
| + fieldConstants.map((e) => e.value).toList()), |
| + type, |
| + constructor, |
| + selector, |
| + concreteArguments.map((e) => e.expression).toList())); |
| } |
| - Constant visitParenthesizedExpression(ParenthesizedExpression node) { |
| + EvaluatedConstant visitParenthesizedExpression(ParenthesizedExpression node) { |
| return node.expression.accept(this); |
| } |
| @@ -671,7 +828,7 @@ class CompileTimeConstantEvaluator extends Visitor { |
| compiler.reportFatalError(node, message); |
| } |
| - Constant signalNotCompileTimeConstant(Node node, |
| + EvaluatedConstant signalNotCompileTimeConstant(Node node, |
| {MessageKind message: MessageKind.NOT_A_COMPILE_TIME_CONSTANT}) { |
| if (isEvaluatingConstant) { |
| error(node, message); |
| @@ -688,8 +845,8 @@ class CompileTimeConstantEvaluator extends Visitor { |
| class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| final InterfaceType constructedType; |
| final ConstructorElement constructor; |
| - final Map<Element, Constant> definitions; |
| - final Map<Element, Constant> fieldValues; |
| + final Map<Element, EvaluatedConstant> definitions; |
| + final Map<Element, EvaluatedConstant> fieldValues; |
| /** |
| * Documentation wanted -- johnniwinther |
| @@ -701,8 +858,8 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| ConstantCompiler handler, |
| Compiler compiler) |
| : this.constructor = constructor, |
| - this.definitions = new Map<Element, Constant>(), |
| - this.fieldValues = new Map<Element, Constant>(), |
| + this.definitions = new Map<Element, EvaluatedConstant>(), |
| + this.fieldValues = new Map<Element, EvaluatedConstant>(), |
| super(handler, |
| compiler.resolver.resolveMethodElement(constructor.declaration), |
| compiler, |
| @@ -710,10 +867,10 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| assert(invariant(constructor, constructor.isImplementation)); |
| } |
| - Constant visitSend(Send send) { |
| + EvaluatedConstant visitSend(Send send) { |
| Element element = elements[send]; |
| if (Elements.isLocal(element)) { |
| - Constant constant = definitions[element]; |
| + EvaluatedConstant constant = definitions[element]; |
| if (constant == null) { |
| compiler.internalError(send, "Local variable without value."); |
| } |
| @@ -724,19 +881,23 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| void potentiallyCheckType(Node node, |
| TypedElement element, |
| - Constant constant) { |
| + EvaluatedConstant constant) { |
| if (compiler.enableTypeAssertions) { |
| DartType elementType = element.type.substByContext(constructedType); |
| - DartType constantType = constant.computeType(compiler); |
| + DartType constantType = constant.value.computeType(compiler); |
| if (!constantSystem.isSubtype(compiler, constantType, elementType)) { |
| - compiler.reportFatalError( |
| - node, MessageKind.NOT_ASSIGNABLE, |
| - {'fromType': constantType, 'toType': elementType}); |
| + compiler.withCurrentElement(constant.element, () { |
| + compiler.reportFatalError( |
| + constant.node, MessageKind.NOT_ASSIGNABLE, |
| + {'fromType': constantType, 'toType': elementType}); |
| + }); |
| } |
| } |
| } |
| - void updateFieldValue(Node node, TypedElement element, Constant constant) { |
| + void updateFieldValue(Node node, |
| + TypedElement element, |
| + EvaluatedConstant constant) { |
| potentiallyCheckType(node, element, constant); |
| fieldValues[element] = constant; |
| } |
| @@ -746,23 +907,24 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| * updating the definitions map. If the constructor has field-initializer |
| * parameters (like [:this.x:]), also updates the [fieldValues] map. |
| */ |
| - void assignArgumentsToParameters(List<Constant> arguments) { |
| + void assignArgumentsToParameters(List<EvaluatedConstant> arguments) { |
| // Assign arguments to parameters. |
| FunctionSignature signature = constructor.functionSignature; |
| int index = 0; |
| signature.orderedForEachParameter((ParameterElement parameter) { |
| - Constant argument = arguments[index++]; |
| + EvaluatedConstant argument = arguments[index++]; |
| Node node = parameter.node; |
| - potentiallyCheckType(node, parameter, argument); |
| - definitions[parameter] = argument; |
| if (parameter.isInitializingFormal) { |
| InitializingFormalElement initializingFormal = parameter; |
| updateFieldValue(node, initializingFormal.fieldElement, argument); |
| + } else { |
| + potentiallyCheckType(node, parameter, argument); |
| + definitions[parameter] = argument; |
| } |
| }); |
| } |
| - void evaluateSuperOrRedirectSend(List<Constant> compiledArguments, |
| + void evaluateSuperOrRedirectSend(List<EvaluatedConstant> compiledArguments, |
| FunctionElement targetConstructor) { |
| ConstructorEvaluator evaluator = new ConstructorEvaluator( |
| constructedType.asInstanceOf(targetConstructor.enclosingClass), |
| @@ -780,7 +942,7 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| */ |
| void evaluateConstructorInitializers() { |
| if (constructor.isSynthesized) { |
| - List<Constant> compiledArguments = <Constant>[]; |
| + List<EvaluatedConstant> compiledArguments = <EvaluatedConstant>[]; |
| Function compileArgument = (element) => definitions[element]; |
| Function compileConstant = handler.compileConstant; |
| @@ -808,8 +970,10 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| // A super initializer or constructor redirection. |
| Send call = link.head; |
| FunctionElement target = elements[call]; |
| - List<Constant> compiledArguments = evaluateArgumentsToConstructor( |
| - call, elements.getSelector(call), call.arguments, target); |
| + List<EvaluatedConstant> compiledArguments = |
| + evaluateArgumentsToConstructor( |
| + call, elements.getSelector(call), call.arguments, target, |
| + compileArgument: evaluateConstant); |
| evaluateSuperOrRedirectSend(compiledArguments, target); |
| foundSuperOrRedirect = true; |
| } else { |
| @@ -817,7 +981,7 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| SendSet init = link.head; |
| Link<Node> initArguments = init.arguments; |
| assert(!initArguments.isEmpty && initArguments.tail.isEmpty); |
| - Constant fieldValue = evaluate(initArguments.head); |
| + EvaluatedConstant fieldValue = evaluate(initArguments.head); |
| updateFieldValue(init, elements[init], fieldValue); |
| } |
| } |
| @@ -841,8 +1005,9 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| compiler.internalError(functionNode, |
| "No default constructor available."); |
| } |
| - List<Constant> compiledArguments = evaluateArgumentsToConstructor( |
| - functionNode, selector, const Link<Node>(), targetConstructor); |
| + List<EvaluatedConstant> compiledArguments = |
| + evaluateArgumentsToConstructor( |
| + functionNode, selector, const Link<Node>(), targetConstructor); |
| evaluateSuperOrRedirectSend(compiledArguments, targetConstructor); |
| } |
| } |
| @@ -853,25 +1018,49 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
| * [arguments] to obtain the field values that need to be passed to the |
| * native JavaScript constructor. |
| */ |
| - void evaluateConstructorFieldValues(List<Constant> arguments) { |
| + void evaluateConstructorFieldValues(List<EvaluatedConstant> arguments) { |
| compiler.withCurrentElement(constructor, () { |
| assignArgumentsToParameters(arguments); |
| evaluateConstructorInitializers(); |
| }); |
| } |
| - List<Constant> buildJsNewArguments(ClassElement classElement) { |
| - List<Constant> jsNewArguments = <Constant>[]; |
| + /// Builds a normalized list of the constant values for each field in the |
| + /// inheritance chain of [classElement]. |
| + List<EvaluatedConstant> buildFieldConstants(ClassElement classElement) { |
| + List<EvaluatedConstant> fieldConstants = <EvaluatedConstant>[]; |
| classElement.implementation.forEachInstanceField( |
| - (ClassElement enclosing, Element field) { |
| - Constant fieldValue = fieldValues[field]; |
| + (ClassElement enclosing, FieldElement field) { |
| + EvaluatedConstant fieldValue = fieldValues[field]; |
| if (fieldValue == null) { |
| // Use the default value. |
| - fieldValue = handler.compileConstant(field); |
| + fieldValue = new EvaluatedConstant.fromDefaultValue( |
| + field, handler.compileConstant(field)); |
| } |
| - jsNewArguments.add(fieldValue); |
| + fieldConstants.add(fieldValue); |
| }, |
| includeSuperAndInjectedMembers: true); |
| - return jsNewArguments; |
| + return fieldConstants; |
| } |
| } |
| + |
| +class EvaluatedConstant { |
|
sigurdm
2014/09/17 10:29:40
Document this class esp. why we need it.
Maybe it
Johnni Winther
2014/09/17 12:20:39
Done.
|
| + final Element element; |
| + final Node node; |
| + final ConstExp expression; |
| + |
| + EvaluatedConstant(this.element, this.node, this.expression); |
| + |
| + factory EvaluatedConstant.fromDefaultValue( |
| + VariableElement element, |
| + ConstExp constant) { |
| + return new EvaluatedConstant( |
| + element, |
| + element.initializer != null ? element.initializer : element.node, |
| + constant); |
| + } |
| + |
| + Constant get value => expression.value; |
| + |
| + String toString() => expression.toString(); |
| +} |