| 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 94523f092acdc2a38df11c8bef179891a973a8ec..cc0eac63aafbc4f065a6449c07f68e46dd66af0f 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
|
| @@ -4,14 +4,77 @@
|
|
|
| part of dart2js;
|
|
|
| +/// A [ConstantEnvironment] provides access for constants compiled for variable
|
| +/// initializers.
|
| +abstract class ConstantEnvironment {
|
| + /// Returns the constant for the initializer of [element].
|
| + Constant 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.
|
| + Constant 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.
|
| + Constant 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.
|
| + Constant 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].
|
| + Constant getConstantForNode(Node node, TreeElements elements);
|
| +
|
| + /// Returns the compile-time constant value of [metadata].
|
| + Constant 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 [ConstantHandler] keeps track of compile-time constants,
|
| - * initializations of global and static fields, and default values of
|
| - * optional parameters.
|
| + * 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.
|
| */
|
| -class ConstantHandler extends CompilerTask {
|
| +abstract class ConstantCompilerBase implements ConstantCompiler {
|
| + final Compiler compiler;
|
| final ConstantSystem constantSystem;
|
| - final bool isMetadata;
|
|
|
| /**
|
| * Contains the initial value of fields. Must contain all static and global
|
| @@ -20,66 +83,40 @@ class ConstantHandler extends CompilerTask {
|
| *
|
| * Invariant: The keys in this map are declarations.
|
| */
|
| - final Map<VariableElement, Constant> initialVariableValues;
|
| -
|
| - /** Set of all registered compiled constants. */
|
| - final Set<Constant> compiledConstants;
|
| + final Map<VariableElement, Constant> initialVariableValues =
|
| + new Map<VariableElement, Constant>();
|
|
|
| /** The set of variable elements that are in the process of being computed. */
|
| - final Set<VariableElement> pendingVariables;
|
| -
|
| - /** Caches the statics where the initial value cannot be eagerly compiled. */
|
| - final Set<VariableElement> lazyStatics;
|
| + final Set<VariableElement> pendingVariables = new Set<VariableElement>();
|
|
|
| - ConstantHandler(Compiler compiler, this.constantSystem,
|
| - { bool this.isMetadata: false })
|
| - : initialVariableValues = new Map<VariableElement, dynamic>(),
|
| - compiledConstants = new Set<Constant>(),
|
| - pendingVariables = new Set<VariableElement>(),
|
| - lazyStatics = new Set<VariableElement>(),
|
| - super(compiler);
|
| -
|
| - String get name => 'ConstantHandler';
|
| -
|
| - void addCompileTimeConstantForEmission(Constant constant) {
|
| - compiledConstants.add(constant);
|
| - }
|
| + ConstantCompilerBase(this.compiler, this.constantSystem);
|
|
|
| Constant getConstantForVariable(VariableElement element) {
|
| return initialVariableValues[element.declaration];
|
| }
|
|
|
| - /**
|
| - * Returns a compile-time constant, or reports an error if the element is not
|
| - * a compile-time constant.
|
| - */
|
| Constant compileConstant(VariableElement element) {
|
| return compileVariable(element, isConst: true);
|
| }
|
|
|
| - /**
|
| - * Returns the a compile-time constant if the variable could be compiled
|
| - * eagerly. Otherwise returns `null`.
|
| - */
|
| Constant compileVariable(VariableElement element, {bool isConst: false}) {
|
| - return measure(() {
|
| - if (initialVariableValues.containsKey(element.declaration)) {
|
| - Constant result = initialVariableValues[element.declaration];
|
| - return result;
|
| - }
|
| - Element currentElement = element;
|
| - if (element.isParameter()
|
| - || element.isFieldParameter()
|
| - || element.isVariable()) {
|
| - currentElement = element.enclosingElement;
|
| - }
|
| - return compiler.withCurrentElement(currentElement, () {
|
| - TreeElements definitions =
|
| - compiler.analyzeElement(currentElement.declaration);
|
| - Constant constant = compileVariableWithDefinitions(
|
| - element, definitions, isConst: isConst);
|
| - return constant;
|
| - });
|
| +
|
| + if (initialVariableValues.containsKey(element.declaration)) {
|
| + Constant result = initialVariableValues[element.declaration];
|
| + return result;
|
| + }
|
| + Element currentElement = element;
|
| + if (element.isParameter() ||
|
| + element.isFieldParameter() ||
|
| + element.isVariable()) {
|
| + currentElement = element.enclosingElement;
|
| + }
|
| + return compiler.withCurrentElement(currentElement, () {
|
| + TreeElements definitions =
|
| + compiler.analyzeElement(currentElement.declaration);
|
| + Constant constant = compileVariableWithDefinitions(
|
| + element, definitions, isConst: isConst);
|
| + return constant;
|
| });
|
| }
|
|
|
| @@ -92,149 +129,116 @@ class ConstantHandler extends CompilerTask {
|
| Constant compileVariableWithDefinitions(VariableElement element,
|
| TreeElements definitions,
|
| {bool isConst: false}) {
|
| - return measure(() {
|
| - if (!isConst && lazyStatics.contains(element)) return null;
|
| -
|
| - Node node = element.parseNode(compiler);
|
| - if (pendingVariables.contains(element)) {
|
| - if (isConst) {
|
| - compiler.reportFatalError(
|
| - node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS);
|
| - } else {
|
| - lazyStatics.add(element);
|
| - return null;
|
| - }
|
| + Node node = element.parseNode(compiler);
|
| + if (pendingVariables.contains(element)) {
|
| + if (isConst) {
|
| + compiler.reportFatalError(
|
| + node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS);
|
| }
|
| - pendingVariables.add(element);
|
| + return null;
|
| + }
|
| + pendingVariables.add(element);
|
|
|
| - Expression initializer = element.initializer;
|
| - Constant value;
|
| - if (initializer == null) {
|
| - // No initial value.
|
| - value = new NullConstant();
|
| - } else {
|
| - value = compileNodeWithDefinitions(
|
| - initializer, definitions, isConst: isConst);
|
| - if (compiler.enableTypeAssertions &&
|
| - value != null &&
|
| - element.isField()) {
|
| - DartType elementType = element.type;
|
| - if (elementType.kind == TypeKind.MALFORMED_TYPE && !value.isNull) {
|
| + Expression initializer = element.initializer;
|
| + Constant value;
|
| + if (initializer == null) {
|
| + // No initial value.
|
| + value = new NullConstant();
|
| + } else {
|
| + value = compileNodeWithDefinitions(
|
| + initializer, definitions, isConst: isConst);
|
| + if (compiler.enableTypeAssertions &&
|
| + value != null &&
|
| + element.isField()) {
|
| + DartType elementType = element.type;
|
| + if (elementType.kind == TypeKind.MALFORMED_TYPE && !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.computeType(compiler);
|
| + if (!constantSystem.isSubtype(compiler,
|
| + constantType, elementType)) {
|
| if (isConst) {
|
| - ErroneousElement element = elementType.element;
|
| compiler.reportFatalError(
|
| - node, element.messageKind, element.messageArguments);
|
| + node, MessageKind.NOT_ASSIGNABLE,
|
| + {'fromType': constantType, 'toType': elementType});
|
| } else {
|
| - // We need to throw an exception at runtime.
|
| + // If the field cannot be lazily initialized, we will throw
|
| + // the exception at runtime.
|
| value = null;
|
| }
|
| - } else {
|
| - DartType constantType = 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(!isConst);
|
| - lazyStatics.add(element);
|
| - }
|
| - pendingVariables.remove(element);
|
| - return value;
|
| - });
|
| + }
|
| + if (value != null) {
|
| + initialVariableValues[element.declaration] = value;
|
| + } else {
|
| + assert(!isConst);
|
| + }
|
| + pendingVariables.remove(element);
|
| + return value;
|
| }
|
|
|
| Constant compileNodeWithDefinitions(Node node,
|
| TreeElements definitions,
|
| - {bool isConst: false}) {
|
| - return measure(() {
|
| - assert(node != null);
|
| - Constant constant = definitions.getConstant(node);
|
| - if (constant != null) {
|
| - return constant;
|
| - }
|
| - CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator(
|
| - this, definitions, compiler, isConst: isConst);
|
| - constant = evaluator.evaluate(node);
|
| - if (constant != null) {
|
| - definitions.setConstant(node, constant);
|
| - }
|
| - return constant;
|
| - });
|
| + {bool isConst: true}) {
|
| + assert(node != null);
|
| + CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator(
|
| + this, definitions, compiler, isConst: isConst);
|
| + return evaluator.evaluate(node);
|
| }
|
|
|
| - /**
|
| - * Returns an [Iterable] of static non final fields that need to be
|
| - * initialized. The fields list must be evaluated in order since they might
|
| - * depend on each other.
|
| - */
|
| - Iterable<VariableElement> getStaticNonFinalFieldsForEmission() {
|
| - return initialVariableValues.keys.where((element) {
|
| - return element.kind == ElementKind.FIELD
|
| - && !element.isInstanceMember()
|
| - && !element.modifiers.isFinal()
|
| - // The const fields are all either emitted elsewhere or inlined.
|
| - && !element.modifiers.isConst();
|
| - });
|
| + Constant compileNode(Node node, TreeElements elements) {
|
| + return compileNodeWithDefinitions(node, elements);
|
| }
|
|
|
| - List<VariableElement> getLazilyInitializedFieldsForEmission() {
|
| - return new List<VariableElement>.from(lazyStatics);
|
| + Constant compileMetadata(MetadataAnnotation metadata,
|
| + Node node,
|
| + TreeElements elements) {
|
| + return compileNodeWithDefinitions(node, elements);
|
| }
|
| +}
|
|
|
| - /**
|
| - * Returns a list of constants topologically sorted so that dependencies
|
| - * appear before the dependent constant. [preSortCompare] is a comparator
|
| - * function that gives the constants a consistent order prior to the
|
| - * topological sort which gives the constants an ordering that is less
|
| - * sensitive to perturbations in the source code.
|
| - */
|
| - List<Constant> getConstantsForEmission([preSortCompare]) {
|
| - // We must emit dependencies before their uses.
|
| - Set<Constant> seenConstants = new Set<Constant>();
|
| - List<Constant> result = new List<Constant>();
|
| -
|
| - void addConstant(Constant constant) {
|
| - if (!seenConstants.contains(constant)) {
|
| - constant.getDependencies().forEach(addConstant);
|
| - assert(!seenConstants.contains(constant));
|
| - result.add(constant);
|
| - seenConstants.add(constant);
|
| - }
|
| - }
|
| +/// [ConstantCompiler] that uses the Dart semantics for the compile-time
|
| +/// constant evaluation.
|
| +class DartConstantCompiler extends ConstantCompilerBase {
|
| + DartConstantCompiler(Compiler compiler)
|
| + : super(compiler, const DartConstantSystem());
|
|
|
| - List<Constant> sorted = compiledConstants.toList();
|
| - if (preSortCompare != null) {
|
| - sorted.sort(preSortCompare);
|
| - }
|
| - sorted.forEach(addConstant);
|
| - return result;
|
| + Constant getConstantForNode(Node node, TreeElements definitions) {
|
| + return definitions.getConstant(node);
|
| + }
|
| +
|
| + Constant getConstantForMetadata(MetadataAnnotation metadata) {
|
| + return metadata.value;
|
| }
|
|
|
| - Constant getInitialValueFor(VariableElement element) {
|
| - Constant initialValue = initialVariableValues[element.declaration];
|
| - if (initialValue == null) {
|
| - compiler.internalError(element, "No initial value for given element.");
|
| + Constant compileNodeWithDefinitions(Node node,
|
| + TreeElements definitions,
|
| + {bool isConst: true}) {
|
| + Constant constant = definitions.getConstant(node);
|
| + if (constant != null) {
|
| + return constant;
|
| }
|
| - return initialValue;
|
| + constant =
|
| + super.compileNodeWithDefinitions(node, definitions, isConst: isConst);
|
| + if (constant != null) {
|
| + definitions.setConstant(node, constant);
|
| + }
|
| + return constant;
|
| }
|
| }
|
|
|
| class CompileTimeConstantEvaluator extends Visitor {
|
| bool isEvaluatingConstant;
|
| - final ConstantHandler handler;
|
| + final ConstantCompilerBase handler;
|
| final TreeElements elements;
|
| final Compiler compiler;
|
|
|
| @@ -440,6 +444,14 @@ class CompileTimeConstantEvaluator extends Visitor {
|
| return false;
|
| }
|
|
|
| + Constant visitIdentifier(Identifier node) {
|
| + Element element = elements[node];
|
| + if (Elements.isClass(element) || Elements.isTypedef(element)) {
|
| + return makeTypeConstant(element);
|
| + }
|
| + return signalNotCompileTimeConstant(node);
|
| + }
|
| +
|
| // TODO(floitsch): provide better error-messages.
|
| Constant visitSend(Send send) {
|
| Element element = elements[send];
|
| @@ -477,11 +489,6 @@ class CompileTimeConstantEvaluator extends Visitor {
|
| Constant right = evaluate(send.argumentsNode.nodes.tail.head);
|
| Constant result = constantSystem.identity.fold(left, right);
|
| if (result != null) return result;
|
| - } else if (Elements.isClass(element) || Elements.isTypedef(element)) {
|
| - // The node itself is not a constant but we register the selector (the
|
| - // identifier that refers to the class/typedef) as a constant.
|
| - Constant typeConstant = makeTypeConstant(element);
|
| - elements.setConstant(send.selector, typeConstant);
|
| }
|
| return signalNotCompileTimeConstant(send);
|
| } else if (send.isPrefix) {
|
| @@ -810,7 +817,7 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator {
|
| * Invariant: [constructor] must be an implementation element.
|
| */
|
| ConstructorEvaluator(FunctionElement constructor,
|
| - ConstantHandler handler,
|
| + ConstantCompiler handler,
|
| Compiler compiler)
|
| : this.constructor = constructor,
|
| this.definitions = new Map<Element, Constant>(),
|
| @@ -843,6 +850,8 @@ class ConstructorEvaluator extends CompileTimeConstantEvaluator {
|
| // TODO(ngeoffray): Handle type parameters.
|
| if (elementType.element.isTypeVariable()) return;
|
| if (!constantSystem.isSubtype(compiler, constantType, elementType)) {
|
| + // TODO(johnniwinther): Provide better [node] values that point to the
|
| + // origin of the constant and not (just) the assignment.
|
| compiler.reportFatalError(
|
| node, MessageKind.NOT_ASSIGNABLE,
|
| {'fromType': elementType, 'toType': constantType});
|
|
|