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