| Index: frog/leg/compile_time_constants.dart
|
| ===================================================================
|
| --- frog/leg/compile_time_constants.dart (revision 5925)
|
| +++ frog/leg/compile_time_constants.dart (working copy)
|
| @@ -1,1115 +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.
|
| -
|
| -class Constant implements Hashable {
|
| - const Constant();
|
| -
|
| - bool isNull() => false;
|
| - bool isBool() => false;
|
| - bool isTrue() => false;
|
| - bool isFalse() => false;
|
| - bool isInt() => false;
|
| - bool isDouble() => false;
|
| - bool isNum() => false;
|
| - bool isString() => false;
|
| - bool isList() => false;
|
| - bool isMap() => false;
|
| - bool isConstructedObject() => false;
|
| - /** Returns true if the constant is null, a bool, a number or a string. */
|
| - bool isPrimitive() => false;
|
| - /** Returns true if the constant is a list, a map or a constructed object. */
|
| - bool isObject() => false;
|
| -
|
| - abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler);
|
| - /**
|
| - * Unless the constant can be emitted multiple times (as for numbers and
|
| - * strings) adds its canonical name to the buffer.
|
| - */
|
| - abstract void writeCanonicalizedJsCode(StringBuffer buffer,
|
| - ConstantHandler handler);
|
| - abstract List<Constant> getDependencies();
|
| -}
|
| -
|
| -class PrimitiveConstant extends Constant {
|
| - abstract get value();
|
| - const PrimitiveConstant();
|
| - bool isPrimitive() => true;
|
| -
|
| - bool operator ==(var other) {
|
| - if (other is !PrimitiveConstant) return false;
|
| - PrimitiveConstant otherPrimitive = other;
|
| - // We use == instead of === so that DartStrings compare correctly.
|
| - return value == otherPrimitive.value;
|
| - }
|
| -
|
| - String toString() => value.toString();
|
| - // Primitive constants don't have dependencies.
|
| - List<Constant> getDependencies() => const <Constant>[];
|
| - abstract DartString toDartString();
|
| -
|
| - void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - writeJsCode(buffer, handler);
|
| - }
|
| -}
|
| -
|
| -class NullConstant extends PrimitiveConstant {
|
| - factory NullConstant() => const NullConstant._internal();
|
| - const NullConstant._internal();
|
| - bool isNull() => true;
|
| - get value() => null;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("(void 0)");
|
| - }
|
| -
|
| - // The magic constant has no meaning. It is just a random value.
|
| - int hashCode() => 785965825;
|
| - DartString toDartString() => const LiteralDartString("null");
|
| -}
|
| -
|
| -class NumConstant extends PrimitiveConstant {
|
| - abstract num get value();
|
| - const NumConstant();
|
| - bool isNum() => true;
|
| -}
|
| -
|
| -class IntConstant extends NumConstant {
|
| - final int value;
|
| - factory IntConstant(int value) {
|
| - switch(value) {
|
| - case 0: return const IntConstant._internal(0);
|
| - case 1: return const IntConstant._internal(1);
|
| - case 2: return const IntConstant._internal(2);
|
| - case 3: return const IntConstant._internal(3);
|
| - case 4: return const IntConstant._internal(4);
|
| - case 5: return const IntConstant._internal(5);
|
| - case 6: return const IntConstant._internal(6);
|
| - case 7: return const IntConstant._internal(7);
|
| - case 8: return const IntConstant._internal(8);
|
| - case 9: return const IntConstant._internal(9);
|
| - case 10: return const IntConstant._internal(10);
|
| - case -1: return const IntConstant._internal(-1);
|
| - case -2: return const IntConstant._internal(-2);
|
| - default: return new IntConstant._internal(value);
|
| - }
|
| - }
|
| - const IntConstant._internal(this.value);
|
| - bool isInt() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("$value");
|
| - }
|
| -
|
| - // We have to override the equality operator so that ints and doubles are
|
| - // treated as separate constants.
|
| - // The is [:!IntConstant:] check at the beginning of the function makes sure
|
| - // that we compare only equal to integer constants.
|
| - bool operator ==(var other) {
|
| - if (other is !IntConstant) return false;
|
| - IntConstant otherInt = other;
|
| - return value == otherInt.value;
|
| - }
|
| -
|
| - int hashCode() => value.hashCode();
|
| - DartString toDartString() => new DartString.literal(value.toString());
|
| -}
|
| -
|
| -class DoubleConstant extends NumConstant {
|
| - final double value;
|
| - factory DoubleConstant(double value) {
|
| - if (value.isNaN()) {
|
| - return const DoubleConstant._internal(double.NAN);
|
| - } else if (value == double.INFINITY) {
|
| - return const DoubleConstant._internal(double.INFINITY);
|
| - } else if (value == -double.INFINITY) {
|
| - return const DoubleConstant._internal(-double.INFINITY);
|
| - } else if (value == 0.0 && !value.isNegative()) {
|
| - return const DoubleConstant._internal(0.0);
|
| - } else if (value == 1.0) {
|
| - return const DoubleConstant._internal(1.0);
|
| - } else {
|
| - return new DoubleConstant._internal(value);
|
| - }
|
| - }
|
| - const DoubleConstant._internal(this.value);
|
| - bool isDouble() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - if (value.isNaN()) {
|
| - buffer.add("(0/0)");
|
| - } else if (value == double.INFINITY) {
|
| - buffer.add("(1/0)");
|
| - } else if (value == -double.INFINITY) {
|
| - buffer.add("(-1/0)");
|
| - } else {
|
| - buffer.add("$value");
|
| - }
|
| - }
|
| -
|
| - bool operator ==(var other) {
|
| - if (other is !DoubleConstant) return false;
|
| - DoubleConstant otherDouble = other;
|
| - double otherValue = otherDouble.value;
|
| - if (value == 0.0 && otherValue == 0.0) {
|
| - return value.isNegative() == otherValue.isNegative();
|
| - } else if (value.isNaN()) {
|
| - return otherValue.isNaN();
|
| - } else {
|
| - return value == otherValue;
|
| - }
|
| - }
|
| -
|
| - int hashCode() => value.hashCode();
|
| - DartString toDartString() => new DartString.literal(value.toString());
|
| -}
|
| -
|
| -class BoolConstant extends PrimitiveConstant {
|
| - factory BoolConstant(value) {
|
| - return value ? new TrueConstant() : new FalseConstant();
|
| - }
|
| - const BoolConstant._internal();
|
| - bool isBool() => true;
|
| -
|
| - BoolConstant unaryFold(String op) {
|
| - if (op == "!") return new BoolConstant(!value);
|
| - return null;
|
| - }
|
| -
|
| - abstract BoolConstant negate();
|
| -}
|
| -
|
| -class TrueConstant extends BoolConstant {
|
| - final bool value = true;
|
| -
|
| - factory TrueConstant() => const TrueConstant._internal();
|
| - const TrueConstant._internal() : super._internal();
|
| - bool isTrue() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("true");
|
| - }
|
| -
|
| - FalseConstant negate() => new FalseConstant();
|
| -
|
| - bool operator ==(var other) => this === other;
|
| - // The magic constant is just a random value. It does not have any
|
| - // significance.
|
| - int hashCode() => 499;
|
| - DartString toDartString() => const LiteralDartString("true");
|
| -}
|
| -
|
| -class FalseConstant extends BoolConstant {
|
| - final bool value = false;
|
| -
|
| - factory FalseConstant() => const FalseConstant._internal();
|
| - const FalseConstant._internal() : super._internal();
|
| - bool isFalse() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("false");
|
| - }
|
| -
|
| - TrueConstant negate() => new TrueConstant();
|
| -
|
| - bool operator ==(var other) => this === other;
|
| - // The magic constant is just a random value. It does not have any
|
| - // significance.
|
| - int hashCode() => 536555975;
|
| - DartString toDartString() => const LiteralDartString("false");
|
| -}
|
| -
|
| -class StringConstant extends PrimitiveConstant {
|
| - final DartString value;
|
| - int _hashCode;
|
| -
|
| - StringConstant(this.value) {
|
| - // TODO(floitsch): cache StringConstants.
|
| - // TODO(floitsch): compute hashcode without calling toString() on the
|
| - // DartString.
|
| - _hashCode = value.slowToString().hashCode();
|
| - }
|
| - bool isString() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("'");
|
| - ConstantHandler.writeEscapedString(value, buffer, (reason) {
|
| - throw new CompilerCancelledException(reason);
|
| - });
|
| - buffer.add("'");
|
| - }
|
| -
|
| - bool operator ==(var other) {
|
| - if (other is !StringConstant) return false;
|
| - StringConstant otherString = other;
|
| - return (_hashCode == otherString._hashCode) && (value == otherString.value);
|
| - }
|
| -
|
| - int hashCode() => _hashCode;
|
| - DartString toDartString() => value;
|
| -}
|
| -
|
| -class ObjectConstant extends Constant {
|
| - final Type type;
|
| -
|
| - ObjectConstant(this.type);
|
| - bool isObject() => true;
|
| -
|
| - // TODO(1603): The class should be marked as abstract, but the VM doesn't
|
| - // currently allow this.
|
| - abstract int hashCode();
|
| -
|
| - void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - String name = handler.getNameForConstant(this);
|
| - String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype";
|
| - buffer.add("$isolatePrototype.$name");
|
| - }
|
| -}
|
| -
|
| -class ListConstant extends ObjectConstant {
|
| - final List<Constant> entries;
|
| - int _hashCode;
|
| -
|
| - ListConstant(Type type, this.entries) : super(type) {
|
| - // TODO(floitsch): create a better hash.
|
| - int hash = 0;
|
| - for (Constant input in entries) hash ^= input.hashCode();
|
| - _hashCode = hash;
|
| - }
|
| - bool isList() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - // TODO(floitsch): we should not need to go through the compiler to make
|
| - // the list constant.
|
| - String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype";
|
| - buffer.add("$isolatePrototype.makeConstantList");
|
| - buffer.add("([");
|
| - for (int i = 0; i < entries.length; i++) {
|
| - if (i != 0) buffer.add(", ");
|
| - Constant entry = entries[i];
|
| - entry.writeCanonicalizedJsCode(buffer, handler);
|
| - }
|
| - buffer.add("])");
|
| - }
|
| -
|
| - bool operator ==(var other) {
|
| - if (other is !ListConstant) return false;
|
| - ListConstant otherList = other;
|
| - if (hashCode() != otherList.hashCode()) return false;
|
| - // TODO(floitsch): verify that the generic types are the same.
|
| - if (entries.length != otherList.entries.length) return false;
|
| - for (int i = 0; i < entries.length; i++) {
|
| - if (entries[i] != otherList.entries[i]) return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - int hashCode() => _hashCode;
|
| -
|
| - List<Constant> getDependencies() => entries;
|
| -}
|
| -
|
| -class MapConstant extends ObjectConstant {
|
| - /** The dart class implementing constant map literals. */
|
| - static final SourceString DART_CLASS = const SourceString("ConstantMap");
|
| - static final SourceString LENGTH_NAME = const SourceString("length");
|
| - static final SourceString JS_OBJECT_NAME = const SourceString("_jsObject");
|
| - static final SourceString KEYS_NAME = const SourceString("_keys");
|
| -
|
| - final ListConstant keys;
|
| - final List<Constant> values;
|
| - int _hashCode;
|
| -
|
| - MapConstant(Type type, this.keys, this.values) : super(type) {
|
| - // TODO(floitsch): create a better hash.
|
| - int hash = 0;
|
| - for (Constant value in values) hash ^= value.hashCode();
|
| - _hashCode = hash;
|
| - }
|
| - bool isMap() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| -
|
| - void writeJsMap() {
|
| - buffer.add("{");
|
| - for (int i = 0; i < keys.entries.length; i++) {
|
| - if (i != 0) buffer.add(", ");
|
| -
|
| - StringConstant key = keys.entries[i];
|
| - key.writeJsCode(buffer, handler);
|
| - buffer.add(": ");
|
| - Constant value = values[i];
|
| - value.writeCanonicalizedJsCode(buffer, handler);
|
| - }
|
| - buffer.add("}");
|
| - }
|
| -
|
| - void badFieldCountError() {
|
| - handler.compiler.internalError(
|
| - "Compiler and ConstantMap disagree on number of fields.");
|
| - }
|
| -
|
| - ClassElement classElement = type.element;
|
| - buffer.add("new ");
|
| - buffer.add(handler.getJsConstructor(classElement));
|
| - buffer.add("(");
|
| - // The arguments of the JavaScript constructor for any given Dart class
|
| - // are in the same order as the members of the class element.
|
| - int emittedArgumentCount = 0;
|
| - for (Element element in classElement.members) {
|
| - if (element.name == LENGTH_NAME) {
|
| - buffer.add(keys.entries.length);
|
| - } else if (element.name == JS_OBJECT_NAME) {
|
| - writeJsMap();
|
| - } else if (element.name == KEYS_NAME) {
|
| - keys.writeCanonicalizedJsCode(buffer, handler);
|
| - } else {
|
| - // Skip methods.
|
| - if (element.kind == ElementKind.FIELD) badFieldCountError();
|
| - continue;
|
| - }
|
| - emittedArgumentCount++;
|
| - if (emittedArgumentCount == 3) {
|
| - break; // All arguments have been emitted.
|
| - } else {
|
| - buffer.add(", ");
|
| - }
|
| - }
|
| - if (emittedArgumentCount != 3) badFieldCountError();
|
| - buffer.add(")");
|
| - }
|
| -
|
| - bool operator ==(var other) {
|
| - if (other is !MapConstant) return false;
|
| - MapConstant otherMap = other;
|
| - if (hashCode() != otherMap.hashCode()) return false;
|
| - // TODO(floitsch): verify that the generic types are the same.
|
| - if (keys != otherMap.keys) return false;
|
| - for (int i = 0; i < values.length; i++) {
|
| - if (values[i] != otherMap.values[i]) return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - int hashCode() => _hashCode;
|
| -
|
| - List<Constant> getDependencies() {
|
| - List<Constant> result = <Constant>[keys];
|
| - result.addAll(values);
|
| - return result;
|
| - }
|
| -}
|
| -
|
| -class ConstructedConstant extends ObjectConstant {
|
| - final List<Constant> fields;
|
| - int _hashCode;
|
| -
|
| - ConstructedConstant(Type type, this.fields) : super(type) {
|
| - assert(type !== null);
|
| - // TODO(floitsch): create a better hash.
|
| - int hash = 0;
|
| - for (Constant field in fields) {
|
| - hash ^= field.hashCode();
|
| - }
|
| - hash ^= type.element.hashCode();
|
| - _hashCode = hash;
|
| - }
|
| - bool isConstructedObject() => true;
|
| -
|
| - void writeJsCode(StringBuffer buffer, ConstantHandler handler) {
|
| - buffer.add("new ");
|
| - buffer.add(handler.getJsConstructor(type.element));
|
| - buffer.add("(");
|
| - for (int i = 0; i < fields.length; i++) {
|
| - if (i != 0) buffer.add(", ");
|
| - Constant field = fields[i];
|
| - field.writeCanonicalizedJsCode(buffer, handler);
|
| - }
|
| - buffer.add(")");
|
| - }
|
| -
|
| - bool operator ==(var otherVar) {
|
| - if (otherVar is !ConstructedConstant) return false;
|
| - ConstructedConstant other = otherVar;
|
| - if (hashCode() != other.hashCode()) return false;
|
| - // TODO(floitsch): verify that the (generic) types are the same.
|
| - if (type.element != other.type.element) return false;
|
| - if (fields.length != other.fields.length) return false;
|
| - for (int i = 0; i < fields.length; i++) {
|
| - if (fields[i] != other.fields[i]) return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - int hashCode() => _hashCode;
|
| - List<Constant> getDependencies() => fields;
|
| -}
|
| -
|
| -/**
|
| - * The [ConstantHandler] keeps track of compile-time constants,
|
| - * initializations of global and static fields, and default values of
|
| - * optional parameters.
|
| - */
|
| -class ConstantHandler extends CompilerTask {
|
| - // Contains the initial value of fields. Must contain all static and global
|
| - // initializations of used fields. May contain caches for instance fields.
|
| - final Map<VariableElement, Constant> initialVariableValues;
|
| -
|
| - // Map from compile-time constants to their JS name.
|
| - final Map<Constant, String> compiledConstants;
|
| -
|
| - // The set of variable elements that are in the process of being computed.
|
| - final Set<VariableElement> pendingVariables;
|
| -
|
| - ConstantHandler(Compiler compiler)
|
| - : initialVariableValues = new Map<VariableElement, Dynamic>(),
|
| - compiledConstants = new Map<Constant, String>(),
|
| - pendingVariables = new Set<VariableElement>(),
|
| - super(compiler);
|
| - String get name() => 'ConstantHandler';
|
| -
|
| - void registerCompileTimeConstant(Constant constant) {
|
| - Function ifAbsentThunk = (() => compiler.namer.getFreshGlobalName("CTC"));
|
| - compiledConstants.putIfAbsent(constant, ifAbsentThunk);
|
| - }
|
| -
|
| - /**
|
| - * Compiles the initial value of the given field and stores it in an internal
|
| - * map.
|
| - *
|
| - * [WorkItem] must contain a [VariableElement] refering to a global or
|
| - * static field.
|
| - */
|
| - void compileWorkItem(WorkItem work) {
|
| - measure(() {
|
| - assert(work.element.kind == ElementKind.FIELD
|
| - || work.element.kind == ElementKind.PARAMETER
|
| - || work.element.kind == ElementKind.FIELD_PARAMETER);
|
| - VariableElement element = work.element;
|
| - // Shortcut if it has already been compiled.
|
| - if (initialVariableValues.containsKey(element)) return;
|
| - compileVariableWithDefinitions(element, work.resolutionTree);
|
| - assert(pendingVariables.isEmpty());
|
| - });
|
| - }
|
| -
|
| - Constant compileVariable(VariableElement element) {
|
| - return measure(() {
|
| - if (initialVariableValues.containsKey(element)) {
|
| - Constant result = initialVariableValues[element];
|
| - return result;
|
| - }
|
| - TreeElements definitions = compiler.analyzeElement(element);
|
| - Constant constant = compileVariableWithDefinitions(element, definitions);
|
| - return constant;
|
| - });
|
| - }
|
| -
|
| - Constant compileVariableWithDefinitions(VariableElement element,
|
| - TreeElements definitions) {
|
| - return measure(() {
|
| - Node node = element.parseNode(compiler);
|
| - if (pendingVariables.contains(element)) {
|
| - MessageKind kind = MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS;
|
| - compiler.reportError(node,
|
| - new CompileTimeConstantError(kind, const []));
|
| - }
|
| - pendingVariables.add(element);
|
| -
|
| - SendSet assignment = node.asSendSet();
|
| - Constant value;
|
| - if (assignment === null) {
|
| - // No initial value.
|
| - value = new NullConstant();
|
| - } else {
|
| - Node right = assignment.arguments.head;
|
| - value = compileNodeWithDefinitions(right, definitions);
|
| - }
|
| - initialVariableValues[element] = value;
|
| - pendingVariables.remove(element);
|
| - return value;
|
| - });
|
| - }
|
| -
|
| - Constant compileNodeWithDefinitions(Node node, TreeElements definitions) {
|
| - return measure(() {
|
| - assert(node !== null);
|
| - CompileTimeConstantEvaluator evaluator =
|
| - new CompileTimeConstantEvaluator(definitions, compiler);
|
| - return evaluator.evaluate(node);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Returns a [List] of static non final fields that need to be initialized.
|
| - * The list must be evaluated in order since the fields might depend on each
|
| - * other.
|
| - */
|
| - List<VariableElement> getStaticNonFinalFieldsForEmission() {
|
| - return initialVariableValues.getKeys().filter((element) {
|
| - return element.kind == ElementKind.FIELD
|
| - && !element.isInstanceMember()
|
| - && !element.modifiers.isFinal();
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Returns a [List] of static final fields that need to be initialized. The
|
| - * list must be evaluated in order since the fields might depend on each
|
| - * other.
|
| - */
|
| - List<VariableElement> getStaticFinalFieldsForEmission() {
|
| - return initialVariableValues.getKeys().filter((element) {
|
| - return element.kind == ElementKind.FIELD
|
| - && !element.isInstanceMember()
|
| - && element.modifiers.isFinal();
|
| - });
|
| - }
|
| -
|
| - List<Constant> getConstantsForEmission() {
|
| - // 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);
|
| - }
|
| - }
|
| -
|
| - compiledConstants.forEach((Constant key, ignored) => addConstant(key));
|
| - return result;
|
| - }
|
| -
|
| - String getNameForConstant(Constant constant) {
|
| - return compiledConstants[constant];
|
| - }
|
| -
|
| - StringBuffer writeJsCode(StringBuffer buffer, Constant value) {
|
| - value.writeJsCode(buffer, this);
|
| - return buffer;
|
| - }
|
| -
|
| - StringBuffer writeJsCodeForVariable(StringBuffer buffer,
|
| - VariableElement element) {
|
| - if (!initialVariableValues.containsKey(element)) {
|
| - compiler.internalError("No initial value for given element",
|
| - element: element);
|
| - }
|
| - Constant constant = initialVariableValues[element];
|
| - if (constant.isObject()) {
|
| - String name = compiledConstants[constant];
|
| - buffer.add("${compiler.namer.ISOLATE}.prototype.$name");
|
| - } else {
|
| - writeJsCode(buffer, constant);
|
| - }
|
| - return buffer;
|
| - }
|
| -
|
| - /**
|
| - * Write the contents of the quoted string to a [StringBuffer] in
|
| - * a form that is valid as JavaScript string literal content.
|
| - * The string is assumed quoted by single quote characters.
|
| - */
|
| - static void writeEscapedString(DartString string,
|
| - StringBuffer buffer,
|
| - void cancel(String reason)) {
|
| - Iterator<int> iterator = string.iterator();
|
| - while (iterator.hasNext()) {
|
| - int code = iterator.next();
|
| - if (code === $SQ) {
|
| - buffer.add(@"\'");
|
| - } else if (code === $LF) {
|
| - buffer.add(@'\n');
|
| - } else if (code === $CR) {
|
| - buffer.add(@'\r');
|
| - } else if (code === $LS) {
|
| - // This Unicode line terminator and $PS are invalid in JS string
|
| - // literals.
|
| - buffer.add(@'\u2028');
|
| - } else if (code === $PS) {
|
| - buffer.add(@'\u2029');
|
| - } else if (code === $BACKSLASH) {
|
| - buffer.add(@'\\');
|
| - } else {
|
| - if (code > 0xffff) {
|
| - cancel('Unhandled non-BMP character: U+${code.toRadixString(16)}');
|
| - }
|
| - // TODO(lrn): Consider whether all codes above 0x7f really need to
|
| - // be escaped. We build a Dart string here, so it should be a literal
|
| - // stage that converts it to, e.g., UTF-8 for a JS interpreter.
|
| - if (code < 0x20) {
|
| - buffer.add(@'\x');
|
| - if (code < 0x10) buffer.add('0');
|
| - buffer.add(code.toRadixString(16));
|
| - } else if (code >= 0x80) {
|
| - if (code < 0x100) {
|
| - buffer.add(@'\x');
|
| - buffer.add(code.toRadixString(16));
|
| - } else {
|
| - buffer.add(@'\u');
|
| - if (code < 0x1000) {
|
| - buffer.add('0');
|
| - }
|
| - buffer.add(code.toRadixString(16));
|
| - }
|
| - } else {
|
| - buffer.add(new String.fromCharCodes(<int>[code]));
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - String getJsConstructor(ClassElement element) {
|
| - return compiler.namer.isolatePropertyAccess(element);
|
| - }
|
| -}
|
| -
|
| -class CompileTimeConstantEvaluator extends AbstractVisitor {
|
| - final TreeElements elements;
|
| - final Compiler compiler;
|
| -
|
| - CompileTimeConstantEvaluator(this.elements, this.compiler);
|
| -
|
| - Constant evaluate(Node node) {
|
| - return node.accept(this);
|
| - }
|
| -
|
| - visitNode(Node node) {
|
| - error(node);
|
| - }
|
| -
|
| - Constant visitLiteralBool(LiteralBool node) {
|
| - return new BoolConstant(node.value);
|
| - }
|
| -
|
| - Constant visitLiteralDouble(LiteralDouble node) {
|
| - return new DoubleConstant(node.value);
|
| - }
|
| -
|
| - Constant visitLiteralInt(LiteralInt node) {
|
| - return new IntConstant(node.value);
|
| - }
|
| -
|
| - Constant visitLiteralList(LiteralList node) {
|
| - if (!node.isConst()) error(node);
|
| - List<Constant> arguments = <Constant>[];
|
| - for (Link<Node> link = node.elements.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - arguments.add(evaluate(link.head));
|
| - }
|
| - // TODO(floitsch): get type from somewhere.
|
| - Type type = null;
|
| - Constant constant = new ListConstant(type, arguments);
|
| - compiler.constantHandler.registerCompileTimeConstant(constant);
|
| - return constant;
|
| - }
|
| -
|
| - Constant visitLiteralMap(LiteralMap node) {
|
| - // TODO(floitsch): check for isConst, once the parser adds it into the node.
|
| - // if (!node.isConst()) error(node);
|
| - List<StringConstant> keys = <StringConstant>[];
|
| - List<Constant> values = <Constant>[];
|
| - bool hasProtoKey = false;
|
| - for (Link<Node> link = node.entries.nodes;
|
| - !link.isEmpty();
|
| - link = link.tail) {
|
| - LiteralMapEntry entry = link.head;
|
| - Constant key = evaluate(entry.key);
|
| - if (!key.isString() || entry.key.asLiteralString() === null) {
|
| - MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL;
|
| - compiler.reportError(entry.key, new ResolutionError(kind, const []));
|
| - }
|
| - // TODO(floitsch): make this faster.
|
| - StringConstant keyConstant = key;
|
| - if (keyConstant.value == new LiteralDartString("__proto__")) {
|
| - hasProtoKey = true;
|
| - }
|
| - keys.add(key);
|
| - values.add(evaluate(entry.value));
|
| - }
|
| - if (hasProtoKey) {
|
| - compiler.unimplemented("visitLiteralMap with __proto__ key",
|
| - node: node);
|
| - }
|
| - // TODO(floitsch): this should be a List<String> type.
|
| - Type keysType = null;
|
| - ListConstant keysList = new ListConstant(keysType, keys);
|
| - compiler.constantHandler.registerCompileTimeConstant(keysList);
|
| - ClassElement classElement =
|
| - compiler.jsHelperLibrary.find(MapConstant.DART_CLASS);
|
| - classElement.ensureResolved(compiler);
|
| - // TODO(floitsch): copy over the generic type.
|
| - Type type = new SimpleType(classElement.name, classElement);
|
| - compiler.registerInstantiatedClass(classElement);
|
| - Constant constant = new MapConstant(type, keysList, values);
|
| - compiler.constantHandler.registerCompileTimeConstant(constant);
|
| - return constant;
|
| - }
|
| -
|
| - Constant visitLiteralNull(LiteralNull node) {
|
| - return new NullConstant();
|
| - }
|
| -
|
| - Constant visitLiteralString(LiteralString node) {
|
| - return new StringConstant(node.dartString);
|
| - }
|
| -
|
| - Constant visitStringJuxtaposition(StringJuxtaposition node) {
|
| - StringConstant left = evaluate(node.first);
|
| - StringConstant right = evaluate(node.second);
|
| - return new StringConstant(new DartString.concat(left.value, right.value));
|
| - }
|
| -
|
| - Constant visitStringInterpolation(StringInterpolation node) {
|
| - StringConstant initialString = evaluate(node.string);
|
| - DartString accumulator = initialString.value;
|
| - for (StringInterpolationPart part in node.parts) {
|
| - Constant expression = evaluate(part.expression);
|
| - DartString expressionString;
|
| - if (expression.isNum() || expression.isBool()) {
|
| - Object value = expression.value;
|
| - expressionString = new DartString.literal(value.toString());
|
| - } else if (expression.isString()) {
|
| - expressionString = expression.value;
|
| - } else {
|
| - error(part.expression);
|
| - }
|
| - accumulator = new DartString.concat(accumulator, expressionString);
|
| - StringConstant partString = evaluate(part.string);
|
| - accumulator = new DartString.concat(accumulator, partString.value);
|
| - };
|
| - return new StringConstant(accumulator);
|
| - }
|
| -
|
| - // TODO(floitsch): provide better error-messages.
|
| - Constant visitSend(Send send) {
|
| - Element element = elements[send];
|
| - if (Elements.isStaticOrTopLevelField(element)) {
|
| - if (element.modifiers === null ||
|
| - !element.modifiers.isFinal()) {
|
| - error(send);
|
| - }
|
| - return compiler.compileVariable(element);
|
| - } else if (send.isPrefix) {
|
| - assert(send.isOperator);
|
| - Constant receiverConstant = evaluate(send.receiver);
|
| - Operator op = send.selector;
|
| - Constant folded;
|
| - switch (op.source.stringValue) {
|
| - case "!":
|
| - folded = const NotOperation().fold(receiverConstant);
|
| - break;
|
| - case "-":
|
| - folded = const NegateOperation().fold(receiverConstant);
|
| - break;
|
| - case "~":
|
| - folded = const BitNotOperation().fold(receiverConstant);
|
| - break;
|
| - default:
|
| - compiler.internalError("Unexpected operator.", node: op);
|
| - break;
|
| - }
|
| - if (folded === null) error(send);
|
| - return folded;
|
| - } else if (send.isOperator && !send.isPostfix) {
|
| - assert(send.argumentCount() == 1);
|
| - Constant left = evaluate(send.receiver);
|
| - Constant right = evaluate(send.argumentsNode.nodes.head);
|
| - Operator op = send.selector.asOperator();
|
| - Constant folded;
|
| - switch (op.source.stringValue) {
|
| - case "+":
|
| - if (left.isString() && !right.isString()) {
|
| - // At the moment only compile-time concatenation of two strings is
|
| - // allowed.
|
| - error(send);
|
| - }
|
| - folded = const AddOperation().fold(left, right);
|
| - break;
|
| - case "-":
|
| - folded = const SubtractOperation().fold(left, right);
|
| - break;
|
| - case "*":
|
| - folded = const MultiplyOperation().fold(left, right);
|
| - break;
|
| - case "/":
|
| - folded = const DivideOperation().fold(left, right);
|
| - break;
|
| - case "%":
|
| - folded = const ModuloOperation().fold(left, right);
|
| - break;
|
| - case "~/":
|
| - folded = const TruncatingDivideOperation().fold(left, right);
|
| - break;
|
| - case "|":
|
| - folded = const BitOrOperation().fold(left, right);
|
| - break;
|
| - case "&":
|
| - folded = const BitAndOperation().fold(left, right);
|
| - break;
|
| - case "^":
|
| - folded = const BitXorOperation().fold(left, right);
|
| - break;
|
| - case "||":
|
| - folded = const BooleanOr().fold(left, right);
|
| - break;
|
| - case "&&":
|
| - folded = const BooleanAnd().fold(left, right);
|
| - break;
|
| - case "<<":
|
| - folded = const ShiftLeftOperation().fold(left, right);
|
| - break;
|
| - case ">>":
|
| - folded = const ShiftRightOperation().fold(left, right);
|
| - break;
|
| - case "<":
|
| - folded = const LessOperation().fold(left, right);
|
| - break;
|
| - case "<=":
|
| - folded = const LessEqualOperation().fold(left, right);
|
| - break;
|
| - case ">":
|
| - folded = const GreaterOperation().fold(left, right);
|
| - break;
|
| - case ">=":
|
| - folded = const GreaterEqualOperation().fold(left, right);
|
| - break;
|
| - case "==":
|
| - if (left.isPrimitive() && right.isPrimitive()) {
|
| - folded = const EqualsOperation().fold(left, right);
|
| - }
|
| - break;
|
| - case "===":
|
| - if (left.isPrimitive() && right.isPrimitive()) {
|
| - folded = const IdentityOperation().fold(left, right);
|
| - }
|
| - break;
|
| - case "!=":
|
| - if (left.isPrimitive() && right.isPrimitive()) {
|
| - BoolConstant areEquals = const EqualsOperation().fold(left, right);
|
| - if (areEquals === null) {
|
| - folded = null;
|
| - } else {
|
| - folded = areEquals.negate();
|
| - }
|
| - }
|
| - break;
|
| - case "!==":
|
| - if (left.isPrimitive() && right.isPrimitive()) {
|
| - BoolConstant areIdentical =
|
| - const IdentityOperation().fold(left, right);
|
| - if (areIdentical === null) {
|
| - folded = null;
|
| - } else {
|
| - folded = areIdentical.negate();
|
| - }
|
| - }
|
| - break;
|
| - default:
|
| - compiler.internalError("Unexpected operator.", node: op);
|
| - break;
|
| - }
|
| - if (folded === null) error(send);
|
| - return folded;
|
| - }
|
| - return super.visitSend(send);
|
| - }
|
| -
|
| - visitSendSet(SendSet node) {
|
| - error(node);
|
| - }
|
| -
|
| - /** Returns the list of constants that are passed to the static function. */
|
| - List<Constant> evaluateArgumentsToConstructor(Send send,
|
| - FunctionElement target) {
|
| - FunctionParameters parameters = target.computeParameters(compiler);
|
| - List<Constant> arguments = <Constant>[];
|
| - Selector selector = elements.getSelector(send);
|
| -
|
| - Function compileArgument = evaluate;
|
| - Function compileConstant = compiler.compileVariable;
|
| - bool succeeded = selector.addSendArgumentsToList(
|
| - send, arguments, parameters, compileArgument, compileConstant);
|
| - if (!succeeded) error(send);
|
| - return arguments;
|
| - }
|
| -
|
| - Constant visitNewExpression(NewExpression node) {
|
| - if (!node.isConst()) error(node);
|
| -
|
| - FunctionElement constructor = elements[node.send];
|
| - ClassElement classElement = constructor.enclosingElement;
|
| - if (classElement.isInterface()) {
|
| - compiler.resolver.resolveMethodElement(constructor);
|
| - constructor = constructor.defaultImplementation;
|
| - classElement = constructor.enclosingElement;
|
| - }
|
| -
|
| - List<Constant> arguments =
|
| - evaluateArgumentsToConstructor(node.send, constructor);
|
| - ConstructorEvaluator evaluator =
|
| - new ConstructorEvaluator(constructor, compiler);
|
| - evaluator.evaluateConstructorFieldValues(arguments);
|
| - List<Constant>jsNewArguments = evaluator.buildJsNewArguments(classElement);
|
| -
|
| - compiler.registerInstantiatedClass(classElement);
|
| - // TODO(floitsch): take generic types into account.
|
| - Type type = classElement.computeType(compiler);
|
| - Constant constant = new ConstructedConstant(type, jsNewArguments);
|
| - compiler.constantHandler.registerCompileTimeConstant(constant);
|
| - return constant;
|
| - }
|
| -
|
| - Constant visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - return node.expression.accept(this);
|
| - }
|
| -
|
| - error(Node node) {
|
| - // TODO(floitsch): get the list of constants that are currently compiled
|
| - // and present some kind of stack-trace.
|
| - MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT;
|
| - compiler.reportError(node, new CompileTimeConstantError(kind, const []));
|
| - }
|
| -}
|
| -
|
| -class ConstructorEvaluator extends CompileTimeConstantEvaluator {
|
| - FunctionElement constructor;
|
| - final Map<Element, Constant> definitions;
|
| - final Map<Element, Constant> fieldValues;
|
| -
|
| - ConstructorEvaluator(FunctionElement constructor, Compiler compiler)
|
| - : this.constructor = constructor,
|
| - this.definitions = new Map<Element, Constant>(),
|
| - this.fieldValues = new Map<Element, Constant>(),
|
| - super(compiler.resolver.resolveMethodElement(constructor),
|
| - compiler);
|
| -
|
| - Constant visitSend(Send send) {
|
| - Element element = elements[send];
|
| - if (Elements.isLocal(element)) {
|
| - Constant constant = definitions[element];
|
| - if (constant === null) {
|
| - compiler.internalError("Local variable without value", node: send);
|
| - }
|
| - return constant;
|
| - }
|
| - return super.visitSend(send);
|
| - }
|
| -
|
| - /**
|
| - * 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<Constant> arguments) {
|
| - // Assign arguments to parameters.
|
| - FunctionParameters parameters = constructor.computeParameters(compiler);
|
| - int index = 0;
|
| - parameters.forEachParameter((Element parameter) {
|
| - Constant argument = arguments[index++];
|
| - definitions[parameter] = argument;
|
| - if (parameter.kind == ElementKind.FIELD_PARAMETER) {
|
| - FieldParameterElement fieldParameterElement = parameter;
|
| - fieldValues[fieldParameterElement.fieldElement] = argument;
|
| - }
|
| - });
|
| - }
|
| -
|
| - void evaluateSuperOrRedirectSend(FunctionElement targetConstructor,
|
| - List<Constant> targetArguments) {
|
| - ConstructorEvaluator evaluator =
|
| - new ConstructorEvaluator(targetConstructor, compiler);
|
| - evaluator.evaluateConstructorFieldValues(targetArguments);
|
| - // Copy over the fieldValues from the super/redirect-constructor.
|
| - evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value);
|
| - }
|
| -
|
| - /**
|
| - * Runs through the initializers of the given [constructor] and updates
|
| - * the [fieldValues] map.
|
| - */
|
| - void evaluateConstructorInitializers() {
|
| - FunctionExpression functionNode = constructor.parseNode(compiler);
|
| - 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 targetConstructor = elements[call];
|
| - List<Constant> targetArguments =
|
| - evaluateArgumentsToConstructor(call, targetConstructor);
|
| - evaluateSuperOrRedirectSend(targetConstructor, targetArguments);
|
| - foundSuperOrRedirect = true;
|
| - } else {
|
| - // A field initializer.
|
| - SendSet init = link.head;
|
| - Link<Node> initArguments = init.arguments;
|
| - assert(!initArguments.isEmpty() && initArguments.tail.isEmpty());
|
| - Constant fieldValue = evaluate(initArguments.head);
|
| - fieldValues[elements[init]] = fieldValue;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!foundSuperOrRedirect) {
|
| - // No super initializer found. Try to find the default constructor if
|
| - // the class is not Object.
|
| - ClassElement enclosingClass = constructor.enclosingElement;
|
| - ClassElement superClass = enclosingClass.superclass;
|
| - if (enclosingClass != compiler.objectClass) {
|
| - assert(superClass !== null);
|
| - assert(superClass.isResolved);
|
| - FunctionElement targetConstructor =
|
| - superClass.lookupConstructor(superClass.name);
|
| - if (targetConstructor === null) {
|
| - compiler.internalError("no default constructor available");
|
| - }
|
| - evaluateSuperOrRedirectSend(targetConstructor, const <Constant>[]);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * 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<Constant> arguments) {
|
| - compiler.withCurrentElement(constructor, () {
|
| - assignArgumentsToParameters(arguments);
|
| - evaluateConstructorInitializers();
|
| - });
|
| - }
|
| -
|
| - List<Constant> buildJsNewArguments(ClassElement classElement) {
|
| - List<Constant> jsNewArguments = <Constant>[];
|
| - // TODO(floitsch): share this code with the emitter, so that we don't
|
| - // need to care about the order of fields here.
|
| - while (classElement != compiler.objectClass) {
|
| - for (Element member in classElement.members) {
|
| - if (member.isInstanceMember() && member.kind == ElementKind.FIELD) {
|
| - Constant fieldValue = fieldValues[member];
|
| - if (fieldValue === null) {
|
| - // Use the default value.
|
| - fieldValue = compiler.compileVariable(member);
|
| - }
|
| - jsNewArguments.add(fieldValue);
|
| - }
|
| - }
|
| - classElement = classElement.superclass;
|
| - }
|
| - return jsNewArguments;
|
| - }
|
| -}
|
|
|