Index: frog/leg/compile_time_constants.dart |
diff --git a/frog/leg/compile_time_constants.dart b/frog/leg/compile_time_constants.dart |
index 0cfe424704fd64bc4eb3aff8d67ff9f76859b9cd..54388c91b29b87da2145c400b9b2b6e6f72080ca 100644 |
--- a/frog/leg/compile_time_constants.dart |
+++ b/frog/leg/compile_time_constants.dart |
@@ -20,6 +20,7 @@ class Constant implements Hashable { |
bool isObject() => false; |
abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler); |
+ abstract List<Constant> getDependencies(); |
} |
class PrimitiveConstant extends Constant { |
@@ -34,6 +35,8 @@ class PrimitiveConstant extends Constant { |
} |
String toString() => value.toString(); |
+ // Primitive constants don't have dependencies. |
+ List<Constant> getDependencies() => const <Constant>[]; |
abstract DartString toDartString(); |
} |
@@ -275,7 +278,7 @@ class ListConstant extends ObjectConstant { |
if (other is !ListConstant) return false; |
ListConstant otherList = other; |
if (hashCode() != otherList.hashCode()) return false; |
- // TODO(floitsch): verify that the types are the same. |
+ // 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; |
@@ -284,6 +287,110 @@ class ListConstant extends ObjectConstant { |
} |
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) { |
+ String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; |
+ |
+ 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]; |
+ // TODO(floitsch): share this code with the ListConstant and |
+ // ConstructedConstant. |
+ if (value.isObject()) { |
+ String name = handler.getNameForConstant(value); |
+ buffer.add("$isolatePrototype.$name"); |
+ } else { |
+ value.writeJsCode(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) { |
+ // TODO(floitsch): share this code with the ListConstant and |
+ // ConstructedConstant. |
+ String name = handler.getNameForConstant(keys); |
+ buffer.add("$isolatePrototype.$name"); |
+ } 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 { |
@@ -312,7 +419,7 @@ class ConstructedConstant extends ObjectConstant { |
Constant field = fields[i]; |
// TODO(floitsch): share this code with the ListConstant. |
if (field.isObject()) { |
- String name = handler.getNameForConstant(entry); |
+ String name = handler.getNameForConstant(field); |
buffer.add("$isolatePrototype.$name"); |
} else { |
field.writeJsCode(buffer, handler); |
@@ -335,6 +442,7 @@ class ConstructedConstant extends ObjectConstant { |
} |
int hashCode() => _hashCode; |
+ List<Constant> getDependencies() => fields; |
} |
/** |
@@ -466,7 +574,21 @@ class ConstantHandler extends CompilerTask { |
} |
List<Constant> getConstantsForEmission() { |
- return compiledConstants.getKeys(); |
+ // 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) { |
@@ -586,7 +708,7 @@ class CompileTimeConstantEvaluator extends AbstractVisitor { |
Constant visitLiteralList(LiteralList node) { |
if (!node.isConst()) error(node); |
- List arguments = []; |
+ List<Constant> arguments = <Constant>[]; |
for (Link<Node> link = node.elements.nodes; |
!link.isEmpty(); |
link = link.tail) { |
@@ -598,7 +720,45 @@ class CompileTimeConstantEvaluator extends AbstractVisitor { |
} |
Constant visitLiteralMap(LiteralMap node) { |
- compiler.unimplemented("CompileTimeConstantEvaluator map", node: 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); |
+ 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); |
+ constantHandler.registerCompileTimeConstant(constant); |
+ return constant; |
} |
Constant visitLiteralNull(LiteralNull node) { |