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 |
deleted file mode 100644 |
index 7b0494365797ef2bd23e32e0e1b8c4fad197657e..0000000000000000000000000000000000000000 |
--- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart |
+++ /dev/null |
@@ -1,1101 +0,0 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of dart2js; |
- |
-/// A [ConstantEnvironment] provides access for constants compiled for variable |
-/// initializers. |
-abstract class ConstantEnvironment { |
- /// Returns the constant for the initializer of [element]. |
- ConstantExpression getConstantForVariable(VariableElement element); |
-} |
- |
-/// A class that can compile and provide constants for variables, nodes and |
-/// metadata. |
-abstract class ConstantCompiler extends ConstantEnvironment { |
- /// Compiles the compile-time constant for the initializer of [element], or |
- /// reports an error if the initializer is not a compile-time constant. |
- /// |
- /// Depending on implementation, the constant compiler might also compute |
- /// the compile-time constant for the backend interpretation of constants. |
- /// |
- /// The returned constant is always of the frontend interpretation. |
- ConstantExpression compileConstant(VariableElement element); |
- |
- /// Computes the compile-time constant for the variable initializer, |
- /// if possible. |
- void compileVariable(VariableElement element); |
- |
- /// Compiles the compile-time constant for [node], or reports an error if |
- /// [node] is not a compile-time constant. |
- /// |
- /// Depending on implementation, the constant compiler might also compute |
- /// the compile-time constant for the backend interpretation of constants. |
- /// |
- /// The returned constant is always of the frontend interpretation. |
- ConstantExpression 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. |
- /// |
- /// Depending on implementation, the constant compiler might also compute |
- /// the compile-time constant for the backend interpretation of constants. |
- /// |
- /// The returned constant is always of the frontend interpretation. |
- ConstantExpression compileMetadata(MetadataAnnotation metadata, |
- Node node, |
- TreeElements elements); |
-} |
- |
-/// A [BackendConstantEnvironment] provides access to constants needed for |
-/// backend implementation. |
-abstract class BackendConstantEnvironment extends ConstantEnvironment { |
- /// Returns the compile-time constant associated with [node]. |
- /// |
- /// Depending on implementation, the constant might be stored in [elements]. |
- ConstantExpression getConstantForNode(Node node, TreeElements elements); |
- |
- /// Returns the compile-time constant value of [metadata]. |
- ConstantExpression getConstantForMetadata(MetadataAnnotation metadata); |
-} |
- |
-/// Interface for the task that compiles the constant environments for the |
-/// frontend and backend interpretation of compile-time constants. |
-abstract class ConstantCompilerTask extends CompilerTask |
- implements ConstantCompiler { |
- ConstantCompilerTask(Compiler compiler) : super(compiler); |
-} |
- |
-/** |
- * The [ConstantCompilerBase] is provides base implementation for compilation of |
- * compile-time constants for both the Dart and JavaScript interpretation of |
- * constants. It keeps track of compile-time constants for initializations of |
- * global and static fields, and default values of optional parameters. |
- */ |
-abstract class ConstantCompilerBase implements ConstantCompiler { |
- final Compiler compiler; |
- final ConstantSystem constantSystem; |
- |
- /** |
- * Contains the initial values of fields and default values of parameters. |
- * |
- * Must contain all static and global initializations of const fields. |
- * |
- * May contain eagerly compiled initial values for statics and instance |
- * fields (if those are compile-time constants). |
- * |
- * May contain default parameter values of optional arguments. |
- * |
- * Invariant: The keys in this map are declarations. |
- */ |
- final Map<VariableElement, ConstantExpression> initialVariableValues = |
- new Map<VariableElement, ConstantExpression>(); |
- |
- /** 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); |
- |
- ConstantExpression getConstantForVariable(VariableElement element) { |
- return initialVariableValues[element.declaration]; |
- } |
- |
- ConstantExpression compileConstant(VariableElement element) { |
- return compileVariable(element, isConst: true); |
- } |
- |
- ConstantExpression compileVariable(VariableElement element, |
- {bool isConst: false}) { |
- |
- if (initialVariableValues.containsKey(element.declaration)) { |
- ConstantExpression result = initialVariableValues[element.declaration]; |
- return result; |
- } |
- AstElement currentElement = element.analyzableElement; |
- return compiler.withCurrentElement(currentElement, () { |
- compiler.analyzeElement(currentElement.declaration); |
- ConstantExpression constant = compileVariableWithDefinitions( |
- element, currentElement.resolvedAst.elements, isConst: isConst); |
- return constant; |
- }); |
- } |
- |
- /** |
- * Returns the a compile-time constant if the variable could be compiled |
- * eagerly. If the variable needs to be initialized lazily returns `null`. |
- * If the variable is `const` but cannot be compiled eagerly reports an |
- * error. |
- */ |
- ConstantExpression compileVariableWithDefinitions(VariableElement element, |
- TreeElements definitions, |
- {bool isConst: false}) { |
- Node node = element.node; |
- if (pendingVariables.contains(element)) { |
- if (isConst) { |
- compiler.reportFatalError( |
- node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS); |
- } |
- return null; |
- } |
- pendingVariables.add(element); |
- |
- Expression initializer = element.initializer; |
- ConstantExpression value; |
- if (initializer == null) { |
- // No initial value. |
- value = new PrimitiveConstantExpression(new NullConstantValue()); |
- } else { |
- value = compileNodeWithDefinitions( |
- initializer, definitions, isConst: isConst); |
- if (compiler.enableTypeAssertions && |
- value != null && |
- element.isField) { |
- DartType elementType = element.type; |
- if (elementType.isMalformed && !value.value.isNull) { |
- if (isConst) { |
- ErroneousElement element = elementType.element; |
- compiler.reportFatalError( |
- node, element.messageKind, element.messageArguments); |
- } else { |
- // We need to throw an exception at runtime. |
- value = null; |
- } |
- } else { |
- DartType constantType = value.value.computeType(compiler); |
- if (!constantSystem.isSubtype(compiler, |
- constantType, elementType)) { |
- if (isConst) { |
- compiler.reportFatalError( |
- node, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': constantType, 'toType': elementType}); |
- } else { |
- // If the field cannot be lazily initialized, we will throw |
- // the exception at runtime. |
- value = null; |
- } |
- } |
- } |
- } |
- } |
- if (value != null) { |
- initialVariableValues[element.declaration] = value; |
- } else { |
- assert(invariant(element, !isConst, |
- message: "Variable $element does not compile to a constant.")); |
- } |
- pendingVariables.remove(element); |
- return value; |
- } |
- |
- ConstantExpression compileNodeWithDefinitions(Node node, |
- TreeElements definitions, |
- {bool isConst: true}) { |
- assert(node != null); |
- CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator( |
- this, definitions, compiler, isConst: isConst); |
- AstConstant constant = evaluator.evaluate(node); |
- return constant != null ? constant.expression : null; |
- } |
- |
- ConstantExpression compileNode(Node node, TreeElements elements) { |
- return compileNodeWithDefinitions(node, elements); |
- } |
- |
- ConstantExpression compileMetadata(MetadataAnnotation metadata, |
- Node node, |
- TreeElements elements) { |
- return compileNodeWithDefinitions(node, elements); |
- } |
- |
- void forgetElement(Element element) { |
- initialVariableValues.remove(element); |
- if (element is ScopeContainerElement) { |
- element.forEachLocalMember(initialVariableValues.remove); |
- } |
- if (element is FunctionElement && element.hasFunctionSignature) { |
- element.functionSignature.forEachParameter(this.forgetElement); |
- } |
- } |
-} |
- |
-/// [ConstantCompiler] that uses the Dart semantics for the compile-time |
-/// constant evaluation. |
-class DartConstantCompiler extends ConstantCompilerBase { |
- DartConstantCompiler(Compiler compiler) |
- : super(compiler, const DartConstantSystem()); |
- |
- ConstantExpression getConstantForNode(Node node, TreeElements definitions) { |
- return definitions.getConstant(node); |
- } |
- |
- ConstantExpression getConstantForMetadata(MetadataAnnotation metadata) { |
- return metadata.constant; |
- } |
- |
- ConstantExpression compileNodeWithDefinitions(Node node, |
- TreeElements definitions, |
- {bool isConst: true}) { |
- ConstantExpression constant = definitions.getConstant(node); |
- if (constant != null) { |
- return constant; |
- } |
- constant = |
- super.compileNodeWithDefinitions(node, definitions, isConst: isConst); |
- if (constant != null) { |
- definitions.setConstant(node, constant); |
- } |
- return constant; |
- } |
-} |
- |
-// TODO(johnniwinther): Decouple the creation of [ConstExp] and [Constant] from |
-// front-end AST in order to reuse the evaluation for the shared front-end. |
-class CompileTimeConstantEvaluator extends Visitor<AstConstant> { |
- bool isEvaluatingConstant; |
- final ConstantCompilerBase handler; |
- final TreeElements elements; |
- final Compiler compiler; |
- |
- Element get context => elements.analyzedElement; |
- |
- CompileTimeConstantEvaluator(this.handler, |
- this.elements, |
- this.compiler, |
- {bool isConst: false}) |
- : this.isEvaluatingConstant = isConst; |
- |
- ConstantSystem get constantSystem => handler.constantSystem; |
- |
- AstConstant evaluate(Node node) { |
- return node.accept(this); |
- } |
- |
- AstConstant evaluateConstant(Node node) { |
- bool oldIsEvaluatingConstant = isEvaluatingConstant; |
- isEvaluatingConstant = true; |
- AstConstant result = node.accept(this); |
- isEvaluatingConstant = oldIsEvaluatingConstant; |
- assert(result != null); |
- return result; |
- } |
- |
- AstConstant visitNode(Node node) { |
- return signalNotCompileTimeConstant(node); |
- } |
- |
- AstConstant visitLiteralBool(LiteralBool node) { |
- return new AstConstant( |
- context, node, new PrimitiveConstantExpression( |
- constantSystem.createBool(node.value))); |
- } |
- |
- AstConstant visitLiteralDouble(LiteralDouble node) { |
- return new AstConstant( |
- context, node, new PrimitiveConstantExpression( |
- constantSystem.createDouble(node.value))); |
- } |
- |
- AstConstant visitLiteralInt(LiteralInt node) { |
- return new AstConstant( |
- context, node, new PrimitiveConstantExpression( |
- constantSystem.createInt(node.value))); |
- } |
- |
- AstConstant visitLiteralList(LiteralList node) { |
- if (!node.isConst) { |
- return signalNotCompileTimeConstant(node); |
- } |
- List<ConstantExpression> argumentExpressions = <ConstantExpression>[]; |
- List<ConstantValue> argumentValues = <ConstantValue>[]; |
- for (Link<Node> link = node.elements.nodes; |
- !link.isEmpty; |
- link = link.tail) { |
- AstConstant argument = evaluateConstant(link.head); |
- if (argument == null) { |
- return null; |
- } |
- argumentExpressions.add(argument.expression); |
- argumentValues.add(argument.value); |
- } |
- DartType type = elements.getType(node); |
- return new AstConstant( |
- context, node, new ListConstantExpression( |
- new ListConstantValue(type, argumentValues), |
- type, |
- argumentExpressions)); |
- } |
- |
- AstConstant visitLiteralMap(LiteralMap node) { |
- if (!node.isConst) { |
- return signalNotCompileTimeConstant(node); |
- } |
- List<ConstantExpression> keyExpressions = <ConstantExpression>[]; |
- List<ConstantValue> keyValues = <ConstantValue>[]; |
- Map<ConstantValue, ConstantExpression> map = |
- new Map<ConstantValue, ConstantExpression>(); |
- for (Link<Node> link = node.entries.nodes; |
- !link.isEmpty; |
- link = link.tail) { |
- LiteralMapEntry entry = link.head; |
- AstConstant 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); |
- } |
- AstConstant value = evaluateConstant(entry.value); |
- if (value == null) { |
- return null; |
- } |
- map[key.value] = value.expression; |
- } |
- List<ConstantExpression> valueExpressions = map.values.toList(); |
- InterfaceType type = elements.getType(node); |
- return new AstConstant( |
- context, node, new MapConstantExpression( |
- constantSystem.createMap(compiler, type, keyValues, |
- valueExpressions.map((e) => e.value).toList()), |
- type, |
- keyExpressions, |
- valueExpressions)); |
- } |
- |
- AstConstant visitLiteralNull(LiteralNull node) { |
- return new AstConstant( |
- context, node, new PrimitiveConstantExpression( |
- constantSystem.createNull())); |
- } |
- |
- AstConstant visitLiteralString(LiteralString node) { |
- return new AstConstant( |
- context, node, new PrimitiveConstantExpression( |
- constantSystem.createString(node.dartString))); |
- } |
- |
- AstConstant visitStringJuxtaposition(StringJuxtaposition node) { |
- AstConstant left = evaluate(node.first); |
- AstConstant right = evaluate(node.second); |
- if (left == null || right == null) return null; |
- StringConstantValue leftValue = left.value; |
- StringConstantValue rightValue = right.value; |
- return new AstConstant( |
- context, node, new ConcatenateConstantExpression( |
- constantSystem.createString( |
- new DartString.concat( |
- leftValue.primitiveValue, rightValue.primitiveValue)), |
- [left.expression, right.expression])); |
- } |
- |
- AstConstant visitStringInterpolation(StringInterpolation node) { |
- List<ConstantExpression> subexpressions = <ConstantExpression>[]; |
- AstConstant initialString = evaluate(node.string); |
- if (initialString == null) { |
- return null; |
- } |
- subexpressions.add(initialString.expression); |
- StringConstantValue initialStringValue = initialString.value; |
- DartString accumulator = initialStringValue.primitiveValue; |
- for (StringInterpolationPart part in node.parts) { |
- AstConstant subexpression = evaluate(part.expression); |
- if (subexpression == null) { |
- return null; |
- } |
- subexpressions.add(subexpression.expression); |
- ConstantValue expression = subexpression.value; |
- DartString expressionString; |
- if (expression.isNum || expression.isBool) { |
- PrimitiveConstantValue primitive = expression; |
- expressionString = |
- new DartString.literal(primitive.primitiveValue.toString()); |
- } else if (expression.isString) { |
- PrimitiveConstantValue primitive = expression; |
- expressionString = primitive.primitiveValue; |
- } 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); |
- AstConstant partString = evaluate(part.string); |
- if (partString == null) return null; |
- subexpressions.add(partString.expression); |
- StringConstantValue partStringValue = partString.value; |
- accumulator = |
- new DartString.concat(accumulator, partStringValue.primitiveValue); |
- }; |
- return new AstConstant( |
- context, node, new ConcatenateConstantExpression( |
- constantSystem.createString(accumulator), |
- subexpressions)); |
- } |
- |
- AstConstant visitLiteralSymbol(LiteralSymbol node) { |
- InterfaceType type = compiler.symbolClass.rawType; |
- String text = node.slowNameString; |
- List<AstConstant> arguments = |
- <AstConstant>[new AstConstant(context, node, |
- new PrimitiveConstantExpression(constantSystem.createString( |
- new DartString.literal(text))))]; |
- AstConstant constant = makeConstructedConstant( |
- compiler, handler, context, node, type, compiler.symbolConstructor, |
- new Selector.callConstructor('', null, 1), |
- arguments, arguments); |
- return new AstConstant( |
- context, node, new SymbolConstantExpression(constant.value, text)); |
- } |
- |
- AstConstant makeTypeConstant(Node node, DartType elementType) { |
- DartType constantType = |
- compiler.backend.typeImplementation.computeType(compiler); |
- return new AstConstant( |
- context, node, new TypeConstantExpression( |
- new TypeConstantValue(elementType, constantType), |
- elementType)); |
- } |
- |
- /// Returns true if the prefix of the send resolves to a deferred import |
- /// prefix. |
- bool isDeferredUse(Send send) { |
- if (send == null) return false; |
- return compiler.deferredLoadTask |
- .deferredPrefixElement(send, elements) != null; |
- } |
- |
- AstConstant visitIdentifier(Identifier node) { |
- Element element = elements[node]; |
- if (Elements.isClass(element) || Elements.isTypedef(element)) { |
- TypeDeclarationElement typeDeclarationElement = element; |
- DartType type = typeDeclarationElement.rawType; |
- return makeTypeConstant(node, type); |
- } |
- return signalNotCompileTimeConstant(node); |
- } |
- |
- // TODO(floitsch): provide better error-messages. |
- AstConstant visitSend(Send send) { |
- Element element = elements[send]; |
- if (send.isPropertyAccess) { |
- if (isDeferredUse(send)) { |
- return signalNotCompileTimeConstant(send, |
- message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT); |
- } |
- if (Elements.isStaticOrTopLevelFunction(element)) { |
- return new AstConstant( |
- context, send, new FunctionConstantExpression( |
- new FunctionConstantValue(element), |
- element)); |
- } else if (Elements.isStaticOrTopLevelField(element)) { |
- ConstantExpression result; |
- if (element.isConst) { |
- result = handler.compileConstant(element); |
- } else if (element.isFinal && !isEvaluatingConstant) { |
- result = handler.compileVariable(element); |
- } |
- if (result != null) { |
- return new AstConstant( |
- context, send, |
- new VariableConstantExpression(result.value, element)); |
- } |
- } else if (Elements.isClass(element) || Elements.isTypedef(element)) { |
- assert(elements.isTypeLiteral(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) { |
- ConstantExpression result = handler.compileConstant(element); |
- if (result != null) { |
- return new AstConstant( |
- context, send, |
- new VariableConstantExpression(result.value, element)); |
- } |
- } |
- return signalNotCompileTimeConstant(send); |
- } else if (send.isCall) { |
- if (identical(element, compiler.identicalFunction) |
- && send.argumentCount() == 2) { |
- AstConstant left = evaluate(send.argumentsNode.nodes.head); |
- AstConstant right = evaluate(send.argumentsNode.nodes.tail.head); |
- if (left == null || right == null) { |
- return null; |
- } |
- ConstantValue result = |
- constantSystem.identity.fold(left.value, right.value); |
- if (result != null) { |
- return new AstConstant( |
- context, send, new BinaryConstantExpression(result, |
- left.expression, 'identical', right.expression)); |
- } |
- } |
- return signalNotCompileTimeConstant(send); |
- } else if (send.isPrefix) { |
- assert(send.isOperator); |
- AstConstant 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."); |
- } |
- ConstantValue folded = operation.fold(receiverConstant.value); |
- if (folded == null) { |
- return signalNotCompileTimeConstant(send); |
- } |
- return new AstConstant( |
- context, send, new UnaryConstantExpression(folded, |
- op.source, receiverConstant.expression)); |
- } else if (send.isOperator && !send.isPostfix) { |
- assert(send.argumentCount() == 1); |
- AstConstant left = evaluate(send.receiver); |
- AstConstant right = evaluate(send.argumentsNode.nodes.head); |
- if (left == null || right == null) { |
- return null; |
- } |
- ConstantValue leftValue = left.value; |
- ConstantValue rightValue = right.value; |
- Operator op = send.selector.asOperator(); |
- ConstantValue folded = null; |
- switch (op.source) { |
- case "==": |
- if (leftValue.isPrimitive && rightValue.isPrimitive) { |
- folded = constantSystem.equal.fold(leftValue, rightValue); |
- } |
- break; |
- case "!=": |
- if (leftValue.isPrimitive && rightValue.isPrimitive) { |
- BoolConstantValue areEquals = |
- constantSystem.equal.fold(leftValue, rightValue); |
- if (areEquals == null) { |
- folded = null; |
- } else { |
- folded = areEquals.negate(); |
- } |
- } |
- break; |
- default: |
- BinaryOperation operation = constantSystem.lookupBinary(op.source); |
- if (operation != null) { |
- folded = operation.fold(leftValue, rightValue); |
- } |
- } |
- if (folded == null) { |
- return signalNotCompileTimeConstant(send); |
- } |
- return new AstConstant( |
- context, send, new BinaryConstantExpression(folded, |
- left.expression, op.source, right.expression)); |
- } |
- return signalNotCompileTimeConstant(send); |
- } |
- |
- AstConstant visitConditional(Conditional node) { |
- AstConstant condition = evaluate(node.condition); |
- if (condition == null) { |
- return null; |
- } else if (!condition.value.isBool) { |
- DartType conditionType = condition.value.computeType(compiler); |
- if (isEvaluatingConstant) { |
- compiler.reportFatalError( |
- node.condition, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': conditionType, 'toType': compiler.boolClass.rawType}); |
- } |
- return null; |
- } |
- AstConstant thenExpression = evaluate(node.thenExpression); |
- AstConstant elseExpression = evaluate(node.elseExpression); |
- if (thenExpression == null || elseExpression == null) { |
- return null; |
- } |
- BoolConstantValue boolCondition = condition.value; |
- return new AstConstant( |
- context, node, new ConditionalConstantExpression( |
- boolCondition.primitiveValue |
- ? thenExpression.value |
- : elseExpression.value, |
- condition.expression, |
- thenExpression.expression, |
- elseExpression.expression)); |
- } |
- |
- AstConstant visitSendSet(SendSet node) { |
- return signalNotCompileTimeConstant(node); |
- } |
- |
- /** |
- * 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<AstConstant> evaluateArgumentsToConstructor( |
- Node node, |
- Selector selector, |
- Link<Node> arguments, |
- FunctionElement target, |
- {AstConstant compileArgument(Node node)}) { |
- assert(invariant(node, target.isImplementation)); |
- List<AstConstant> compiledArguments = <AstConstant>[]; |
- |
- AstConstant compileDefaultValue(VariableElement element) { |
- ConstantExpression constant = handler.compileConstant(element); |
- return new AstConstant.fromDefaultValue(element, constant); |
- } |
- target.computeSignature(compiler); |
- bool succeeded = selector.addArgumentsToList(arguments, |
- compiledArguments, |
- target, |
- compileArgument, |
- compileDefaultValue, |
- compiler.world); |
- if (!succeeded) { |
- String name = Elements.constructorNameForDiagnostics( |
- target.enclosingClass.name, target.name); |
- compiler.reportFatalError( |
- node, |
- MessageKind.INVALID_CONSTRUCTOR_ARGUMENTS, |
- {'constructorName': name}); |
- } |
- return compiledArguments; |
- } |
- |
- AstConstant visitNewExpression(NewExpression node) { |
- if (!node.isConst) { |
- return signalNotCompileTimeConstant(node); |
- } |
- |
- Send send = node.send; |
- FunctionElement constructor = elements[send]; |
- if (Elements.isUnresolved(constructor)) { |
- return signalNotCompileTimeConstant(node); |
- } |
- |
- // Deferred types can not be used in const instance creation expressions. |
- // Check if the constructor comes from a deferred library. |
- if (isDeferredUse(node.send.selector.asSend())) { |
- return signalNotCompileTimeConstant(node, |
- message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION); |
- } |
- |
- // TODO(ahe): This is nasty: we must eagerly analyze the |
- // constructor to ensure the redirectionTarget has been computed |
- // correctly. Find a way to avoid this. |
- compiler.analyzeElement(constructor.declaration); |
- |
- InterfaceType type = elements.getType(node); |
- Selector selector = elements.getSelector(send); |
- |
- Map<Node, AstConstant> concreteArgumentMap = |
- <Node, AstConstant>{}; |
- 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); |
- } |
- |
- List<AstConstant> normalizedArguments = |
- evaluateArgumentsToConstructor( |
- node, selector, send.arguments, constructor.implementation, |
- compileArgument: (node) => concreteArgumentMap[node]); |
- List<AstConstant> concreteArguments = |
- concreteArgumentMap.values.toList(); |
- |
- if (constructor == compiler.intEnvironment || |
- constructor == compiler.boolEnvironment || |
- constructor == compiler.stringEnvironment) { |
- |
- AstConstant createEvaluatedConstant(ConstantValue value) { |
- return new AstConstant( |
- context, node, new ConstructedConstantExpresssion( |
- value, |
- type, |
- constructor, |
- elements.getSelector(send), |
- concreteArguments.map((e) => e.expression).toList())); |
- } |
- |
- var firstArgument = normalizedArguments[0].value; |
- ConstantValue defaultValue = normalizedArguments[1].value; |
- |
- if (firstArgument.isNull) { |
- compiler.reportFatalError( |
- send.arguments.head, MessageKind.NULL_NOT_ALLOWED); |
- return null; |
- } |
- |
- if (!firstArgument.isString) { |
- DartType type = defaultValue.computeType(compiler); |
- compiler.reportFatalError( |
- send.arguments.head, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': type, 'toType': compiler.stringClass.rawType}); |
- return null; |
- } |
- |
- if (constructor == compiler.intEnvironment && |
- !(defaultValue.isNull || defaultValue.isInt)) { |
- DartType type = defaultValue.computeType(compiler); |
- compiler.reportFatalError( |
- send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': type, 'toType': compiler.intClass.rawType}); |
- return null; |
- } |
- |
- if (constructor == compiler.boolEnvironment && |
- !(defaultValue.isNull || defaultValue.isBool)) { |
- DartType type = defaultValue.computeType(compiler); |
- compiler.reportFatalError( |
- send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': type, 'toType': compiler.boolClass.rawType}); |
- return null; |
- } |
- |
- if (constructor == compiler.stringEnvironment && |
- !(defaultValue.isNull || defaultValue.isString)) { |
- DartType type = defaultValue.computeType(compiler); |
- compiler.reportFatalError( |
- send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': type, 'toType': compiler.stringClass.rawType}); |
- return null; |
- } |
- |
- String value = |
- compiler.fromEnvironment(firstArgument.primitiveValue.slowToString()); |
- |
- if (value == null) { |
- return createEvaluatedConstant(defaultValue); |
- } else if (constructor == compiler.intEnvironment) { |
- int number = int.parse(value, onError: (_) => null); |
- return createEvaluatedConstant( |
- (number == null) |
- ? defaultValue |
- : constantSystem.createInt(number)); |
- } else if (constructor == compiler.boolEnvironment) { |
- if (value == 'true') { |
- return createEvaluatedConstant(constantSystem.createBool(true)); |
- } else if (value == 'false') { |
- return createEvaluatedConstant(constantSystem.createBool(false)); |
- } else { |
- return createEvaluatedConstant(defaultValue); |
- } |
- } else { |
- assert(constructor == compiler.stringEnvironment); |
- return createEvaluatedConstant( |
- constantSystem.createString(new DartString.literal(value))); |
- } |
- } else { |
- return makeConstructedConstant( |
- compiler, handler, context, |
- node, type, constructor, selector, |
- concreteArguments, normalizedArguments); |
- } |
- } |
- |
- static AstConstant makeConstructedConstant( |
- Compiler compiler, |
- ConstantCompilerBase handler, |
- Element context, |
- Node node, |
- InterfaceType type, |
- ConstructorElement constructor, |
- Selector selector, |
- List<AstConstant> concreteArguments, |
- List<AstConstant> 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); |
- ConstructorElement target = constructor.effectiveTarget; |
- ClassElement classElement = target.enclosingClass; |
- // The constructor must be an implementation to ensure that field |
- // initializers are handled correctly. |
- target = target.implementation; |
- assert(invariant(node, target.isImplementation)); |
- |
- ConstructorEvaluator evaluator = new ConstructorEvaluator( |
- constructedType, target, handler, compiler); |
- evaluator.evaluateConstructorFieldValues(normalizedArguments); |
- List<AstConstant> fieldConstants = |
- evaluator.buildFieldConstants(classElement); |
- |
- return new AstConstant( |
- context, node, new ConstructedConstantExpresssion( |
- new ConstructedConstantValue( |
- constructedType, |
- fieldConstants.map((e) => e.value).toList()), |
- type, |
- constructor, |
- selector, |
- concreteArguments.map((e) => e.expression).toList())); |
- } |
- |
- AstConstant visitParenthesizedExpression(ParenthesizedExpression node) { |
- return node.expression.accept(this); |
- } |
- |
- error(Node node, MessageKind message) { |
- // TODO(floitsch): get the list of constants that are currently compiled |
- // and present some kind of stack-trace. |
- compiler.reportFatalError(node, message); |
- } |
- |
- AstConstant signalNotCompileTimeConstant(Node node, |
- {MessageKind message: MessageKind.NOT_A_COMPILE_TIME_CONSTANT}) { |
- if (isEvaluatingConstant) { |
- error(node, message); |
- } |
- // Else we don't need to do anything. The final handler is only |
- // optimistically trying to compile constants. So it is normal that we |
- // sometimes see non-compile time constants. |
- // Simply return [:null:] which is used to propagate a failing |
- // compile-time compilation. |
- return null; |
- } |
-} |
- |
-class ConstructorEvaluator extends CompileTimeConstantEvaluator { |
- final InterfaceType constructedType; |
- final ConstructorElement constructor; |
- final Map<Element, AstConstant> definitions; |
- final Map<Element, AstConstant> fieldValues; |
- |
- /** |
- * Documentation wanted -- johnniwinther |
- * |
- * Invariant: [constructor] must be an implementation element. |
- */ |
- ConstructorEvaluator(InterfaceType this.constructedType, |
- FunctionElement constructor, |
- ConstantCompiler handler, |
- Compiler compiler) |
- : this.constructor = constructor, |
- this.definitions = new Map<Element, AstConstant>(), |
- this.fieldValues = new Map<Element, AstConstant>(), |
- super(handler, |
- compiler.resolver.resolveMethodElement(constructor.declaration), |
- compiler, |
- isConst: true) { |
- assert(invariant(constructor, constructor.isImplementation)); |
- } |
- |
- AstConstant visitSend(Send send) { |
- Element element = elements[send]; |
- if (Elements.isLocal(element)) { |
- AstConstant constant = definitions[element]; |
- if (constant == null) { |
- compiler.internalError(send, "Local variable without value."); |
- } |
- return constant; |
- } |
- return super.visitSend(send); |
- } |
- |
- void potentiallyCheckType(Node node, |
- TypedElement element, |
- AstConstant constant) { |
- if (compiler.enableTypeAssertions) { |
- DartType elementType = element.type.substByContext(constructedType); |
- DartType constantType = constant.value.computeType(compiler); |
- if (!constantSystem.isSubtype(compiler, constantType, elementType)) { |
- compiler.withCurrentElement(constant.element, () { |
- compiler.reportFatalError( |
- constant.node, MessageKind.NOT_ASSIGNABLE, |
- {'fromType': constantType, 'toType': elementType}); |
- }); |
- } |
- } |
- } |
- |
- void updateFieldValue(Node node, |
- TypedElement element, |
- AstConstant constant) { |
- potentiallyCheckType(node, element, constant); |
- fieldValues[element] = constant; |
- } |
- |
- /** |
- * Given the arguments (a list of constants) assigns them to the parameters, |
- * updating the definitions map. If the constructor has field-initializer |
- * parameters (like [:this.x:]), also updates the [fieldValues] map. |
- */ |
- void assignArgumentsToParameters(List<AstConstant> arguments) { |
- // Assign arguments to parameters. |
- FunctionSignature signature = constructor.functionSignature; |
- int index = 0; |
- signature.orderedForEachParameter((ParameterElement parameter) { |
- AstConstant argument = arguments[index++]; |
- Node node = parameter.node; |
- if (parameter.isInitializingFormal) { |
- InitializingFormalElement initializingFormal = parameter; |
- updateFieldValue(node, initializingFormal.fieldElement, argument); |
- } else { |
- potentiallyCheckType(node, parameter, argument); |
- definitions[parameter] = argument; |
- } |
- }); |
- } |
- |
- void evaluateSuperOrRedirectSend(List<AstConstant> compiledArguments, |
- FunctionElement targetConstructor) { |
- ConstructorEvaluator evaluator = new ConstructorEvaluator( |
- constructedType.asInstanceOf(targetConstructor.enclosingClass), |
- targetConstructor, handler, compiler); |
- evaluator.evaluateConstructorFieldValues(compiledArguments); |
- // Copy over the fieldValues from the super/redirect-constructor. |
- // No need to go through [updateFieldValue] because the |
- // assignments have already been checked in checked mode. |
- evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value); |
- } |
- |
- /** |
- * Runs through the initializers of the given [constructor] and updates |
- * the [fieldValues] map. |
- */ |
- void evaluateConstructorInitializers() { |
- if (constructor.isSynthesized) { |
- List<AstConstant> compiledArguments = <AstConstant>[]; |
- |
- Function compileArgument = (element) => definitions[element]; |
- Function compileConstant = handler.compileConstant; |
- FunctionElement target = constructor.definingConstructor.implementation; |
- Selector.addForwardingElementArgumentsToList(constructor, |
- compiledArguments, |
- target, |
- compileArgument, |
- compileConstant, |
- compiler.world); |
- evaluateSuperOrRedirectSend(compiledArguments, target); |
- return; |
- } |
- FunctionExpression functionNode = constructor.node; |
- NodeList initializerList = functionNode.initializers; |
- |
- bool foundSuperOrRedirect = false; |
- |
- if (initializerList != null) { |
- for (Link<Node> link = initializerList.nodes; |
- !link.isEmpty; |
- link = link.tail) { |
- assert(link.head is Send); |
- if (link.head is !SendSet) { |
- // A super initializer or constructor redirection. |
- Send call = link.head; |
- FunctionElement target = elements[call]; |
- List<AstConstant> compiledArguments = |
- evaluateArgumentsToConstructor( |
- call, elements.getSelector(call), call.arguments, target, |
- compileArgument: evaluateConstant); |
- evaluateSuperOrRedirectSend(compiledArguments, target); |
- foundSuperOrRedirect = true; |
- } else { |
- // A field initializer. |
- SendSet init = link.head; |
- Link<Node> initArguments = init.arguments; |
- assert(!initArguments.isEmpty && initArguments.tail.isEmpty); |
- AstConstant fieldValue = evaluate(initArguments.head); |
- updateFieldValue(init, elements[init], fieldValue); |
- } |
- } |
- } |
- |
- if (!foundSuperOrRedirect) { |
- // No super initializer found. Try to find the default constructor if |
- // the class is not Object. |
- ClassElement enclosingClass = constructor.enclosingClass; |
- ClassElement superClass = enclosingClass.superclass; |
- if (enclosingClass != compiler.objectClass) { |
- assert(superClass != null); |
- assert(superClass.resolutionState == STATE_DONE); |
- |
- Selector selector = |
- new Selector.callDefaultConstructor(enclosingClass.library); |
- |
- FunctionElement targetConstructor = |
- superClass.lookupConstructor(selector); |
- if (targetConstructor == null) { |
- compiler.internalError(functionNode, |
- "No default constructor available."); |
- } |
- List<AstConstant> compiledArguments = |
- evaluateArgumentsToConstructor( |
- functionNode, selector, const Link<Node>(), targetConstructor); |
- evaluateSuperOrRedirectSend(compiledArguments, targetConstructor); |
- } |
- } |
- } |
- |
- /** |
- * Simulates the execution of the [constructor] with the given |
- * [arguments] to obtain the field values that need to be passed to the |
- * native JavaScript constructor. |
- */ |
- void evaluateConstructorFieldValues(List<AstConstant> arguments) { |
- compiler.withCurrentElement(constructor, () { |
- assignArgumentsToParameters(arguments); |
- evaluateConstructorInitializers(); |
- }); |
- } |
- |
- /// Builds a normalized list of the constant values for each field in the |
- /// inheritance chain of [classElement]. |
- List<AstConstant> buildFieldConstants(ClassElement classElement) { |
- List<AstConstant> fieldConstants = <AstConstant>[]; |
- classElement.implementation.forEachInstanceField( |
- (ClassElement enclosing, FieldElement field) { |
- AstConstant fieldValue = fieldValues[field]; |
- if (fieldValue == null) { |
- // Use the default value. |
- fieldValue = new AstConstant.fromDefaultValue( |
- field, handler.compileConstant(field)); |
- } |
- fieldConstants.add(fieldValue); |
- }, |
- includeSuperAndInjectedMembers: true); |
- return fieldConstants; |
- } |
-} |
- |
-/// A constant created from the front-end AST. |
-/// |
-/// [element] and [node] point to the source location of the constant. |
-/// [expression] holds the symbolic constant expression and [value] its constant |
-/// value. |
-/// |
-/// This class differs from [ConstantExpression] in that it is coupled to the |
-/// front-end AST whereas [ConstantExpression] is only coupled to the element |
-/// model. |
-class AstConstant { |
- final Element element; |
- final Node node; |
- final ConstantExpression expression; |
- |
- AstConstant(this.element, this.node, this.expression); |
- |
- factory AstConstant.fromDefaultValue( |
- VariableElement element, |
- ConstantExpression constant) { |
- return new AstConstant( |
- element, |
- element.initializer != null ? element.initializer : element.node, |
- constant); |
- } |
- |
- ConstantValue get value => expression.value; |
- |
- String toString() => expression.toString(); |
-} |