Index: sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart |
index 1b8992a0a158e5fd65054f1dd5947119949d6118..7635c6a409abbaa8cddea6c58ddbbcbbce754fac 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart |
@@ -4,114 +4,84 @@ |
part of js_backend; |
-class ConstantEmitter { |
- ConstantReferenceEmitter _referenceEmitter; |
- ConstantInitializerEmitter _initializerEmitter; |
+class ConstantEmitter implements ConstantVisitor { |
+ final Compiler compiler; |
+ final Namer namer; |
- ConstantEmitter(Compiler compiler, Namer namer) { |
- _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); |
- _initializerEmitter = new ConstantInitializerEmitter( |
- compiler, namer, _referenceEmitter); |
- } |
+ CodeBuffer buffer; |
+ bool shouldEmitCanonicalVersion; |
- /** |
- * Constructs an expression that is a reference to the constant. Uses a |
- * canonical name unless the constant can be emitted multiple times (as for |
- * numbers and strings). |
- */ |
- js.Expression reference(Constant constant) { |
- return _referenceEmitter.generate(constant); |
- } |
+ ConstantEmitter(this.compiler, this.namer); |
/** |
- * Constructs an expression like [reference], but the expression is valid |
- * during isolate initialization. |
+ * Unless the constant can be emitted multiple times (as for numbers and |
+ * strings) use the canonical name. |
*/ |
- js.Expression referenceInInitializationContext(Constant constant) { |
- return _referenceEmitter.generateInInitializationContext(constant); |
+ void emitCanonicalVersionOfConstant(Constant constant, CodeBuffer newBuffer) { |
+ shouldEmitCanonicalVersion = true; |
+ buffer = newBuffer; |
+ _visit(constant); |
} |
/** |
- * Constructs an expression used to initialize a canonicalized constant. |
+ * Emit the JavaScript code of the constant. If the constant must be |
+ * canonicalized this method emits the initialization value. |
*/ |
- js.Expression initializationExpression(Constant constant) { |
- return _initializerEmitter.generate(constant); |
+ void emitJavaScriptCodeForConstant(Constant constant, CodeBuffer newBuffer) { |
+ shouldEmitCanonicalVersion = false; |
+ buffer = newBuffer; |
+ _visit(constant); |
} |
-} |
- |
-/** |
- * Visitor for generating JavaScript expressions to refer to [Constant]s. |
- * Do not use directly, use methods from [ConstantEmitter]. |
- */ |
-class ConstantReferenceEmitter implements ConstantVisitor<js.Expression> { |
- final Compiler compiler; |
- final Namer namer; |
- bool inIsolateInitializationContext = false; |
- |
- ConstantReferenceEmitter(this.compiler, this.namer); |
- js.Expression generate(Constant constant) { |
- inIsolateInitializationContext = false; |
- return _visit(constant); |
+ _visit(Constant constant) { |
+ constant.accept(this); |
} |
- js.Expression generateInInitializationContext(Constant constant) { |
- inIsolateInitializationContext = true; |
- return _visit(constant); |
- } |
- |
- js.Expression _visit(Constant constant) { |
- return constant.accept(this); |
- } |
- |
- js.Expression visitSentinel(SentinelConstant constant) { |
- return new js.VariableUse(namer.CURRENT_ISOLATE); |
+ void visitSentinel(SentinelConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ buffer.add(namer.CURRENT_ISOLATE); |
+ } else { |
+ compiler.internalError( |
+ "The parameter sentinel constant does not need specific JS code"); |
+ } |
} |
- js.Expression visitFunction(FunctionConstant constant) { |
- return inIsolateInitializationContext |
- ? new js.VariableUse(namer.isolatePropertiesAccess(constant.element)) |
- : new js.VariableUse(namer.isolateAccess(constant.element)); |
+ void visitFunction(FunctionConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ buffer.add(namer.isolatePropertiesAccess(constant.element)); |
+ } else { |
+ compiler.internalError( |
+ "The function constant does not need specific JS code"); |
+ } |
} |
- js.Expression visitNull(NullConstant constant) { |
- return new js.LiteralNull(); |
+ void visitNull(NullConstant constant) { |
+ buffer.add("null"); |
} |
- js.Expression visitInt(IntConstant constant) { |
- return new js.LiteralNumber('${constant.value}'); |
+ void visitInt(IntConstant constant) { |
+ buffer.add(constant.value.toString()); |
} |
- js.Expression visitDouble(DoubleConstant constant) { |
+ void visitDouble(DoubleConstant constant) { |
double value = constant.value; |
if (value.isNaN) { |
- return new js.LiteralNumber("(0/0)"); |
+ buffer.add("(0/0)"); |
} else if (value == double.INFINITY) { |
- return new js.LiteralNumber("(1/0)"); |
+ buffer.add("(1/0)"); |
} else if (value == -double.INFINITY) { |
- return new js.LiteralNumber("(-1/0)"); |
+ buffer.add("(-1/0)"); |
} else { |
- return new js.LiteralNumber("$value"); |
+ buffer.add("$value"); |
} |
} |
- js.Expression visitTrue(TrueConstant constant) { |
- if (compiler.enableMinification) { |
- // Use !0 for true. |
- return new js.Prefix("!", new js.LiteralNumber("0")); |
- } else { |
- return new js.LiteralBool(true); |
- } |
- |
+ void visitTrue(TrueConstant constant) { |
+ buffer.add("true"); |
} |
- js.Expression visitFalse(FalseConstant constant) { |
- if (compiler.enableMinification) { |
- // Use !1 for false. |
- return new js.Prefix("!", new js.LiteralNumber("1")); |
- } else { |
- return new js.LiteralBool(false); |
- } |
+ void visitFalse(FalseConstant constant) { |
+ buffer.add("false"); |
} |
/** |
@@ -119,213 +89,139 @@ class ConstantReferenceEmitter implements ConstantVisitor<js.Expression> { |
* a form that is valid as JavaScript string literal content. |
* The string is assumed quoted by double quote characters. |
*/ |
- js.Expression visitString(StringConstant constant) { |
- // TODO(sra): If the string is long *and repeated* (and not on a hot path) |
- // then it should be assigned to a name. We don't have reference counts (or |
- // profile information) here, so this is the wrong place. |
- StringBuffer sb = new StringBuffer(); |
- writeJsonEscapedCharsOn(constant.value.slowToString(), sb); |
- return new js.LiteralString('"$sb"'); |
+ void visitString(StringConstant constant) { |
+ buffer.add('"'); |
+ writeJsonEscapedCharsOn(constant.value.slowToString(), buffer); |
+ buffer.add('"'); |
} |
- js.Expression emitCanonicalVersion(Constant constant) { |
+ void emitCanonicalVersion(Constant constant) { |
String name = namer.constantName(constant); |
- if (inIsolateInitializationContext) { |
- // $ISOLATE.$ISOLATE_PROPERTIES.$name |
- return new js.PropertyAccess.field( |
- new js.PropertyAccess.field( |
- new js.VariableUse(namer.ISOLATE), |
- namer.ISOLATE_PROPERTIES), |
- name); |
- } else { |
- return new js.PropertyAccess.field( |
- new js.VariableUse(namer.CURRENT_ISOLATE), |
- name); |
- } |
- } |
- |
- js.Expression visitList(ListConstant constant) { |
- return emitCanonicalVersion(constant); |
- } |
- |
- js.Expression visitMap(MapConstant constant) { |
- return emitCanonicalVersion(constant); |
+ buffer.add(namer.isolatePropertiesAccessForConstant(name)); |
} |
- js.Expression visitType(TypeConstant constant) { |
- return emitCanonicalVersion(constant); |
- } |
- |
- js.Expression visitConstructed(ConstructedConstant constant) { |
- return emitCanonicalVersion(constant); |
- } |
-} |
- |
-/** |
- * Visitor for generating JavaScript expressions to initialize [Constant]s. |
- * Do not use directly; use methods from [ConstantEmitter]. |
- */ |
-class ConstantInitializerEmitter implements ConstantVisitor<js.Expression> { |
- final Compiler compiler; |
- final Namer namer; |
- final ConstantReferenceEmitter referenceEmitter; |
- |
- ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); |
- |
- js.Expression generate(Constant constant) { |
- return _visit(constant); |
- } |
- |
- js.Expression _visit(Constant constant) { |
- return constant.accept(this); |
- } |
- |
- js.Expression _reference(Constant constant) { |
- return referenceEmitter.generateInInitializationContext(constant); |
- } |
- |
- js.Expression visitSentinel(SentinelConstant constant) { |
- compiler.internalError( |
- "The parameter sentinel constant does not need specific JS code"); |
- } |
- |
- js.Expression visitFunction(FunctionConstant constant) { |
- compiler.internalError( |
- "The function constant does not need specific JS code"); |
- } |
- |
- js.Expression visitNull(NullConstant constant) { |
- return _reference(constant); |
- } |
- |
- js.Expression visitInt(IntConstant constant) { |
- return _reference(constant); |
- } |
- |
- js.Expression visitDouble(DoubleConstant constant) { |
- return _reference(constant); |
- } |
- |
- js.Expression visitTrue(TrueConstant constant) { |
- return _reference(constant); |
- } |
- |
- js.Expression visitFalse(FalseConstant constant) { |
- return _reference(constant); |
- } |
- |
- js.Expression visitString(StringConstant constant) { |
- // TODO(sra): Some larger strings are worth sharing. |
- return _reference(constant); |
- } |
- |
- js.Expression visitList(ListConstant constant) { |
- return new js.Call( |
- new js.PropertyAccess.field( |
- new js.VariableUse(namer.ISOLATE), |
- 'makeConstantList'), |
- [new js.ArrayInitializer.from(_array(constant.entries))]); |
+ void visitList(ListConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ emitCanonicalVersion(constant); |
+ } else { |
+ shouldEmitCanonicalVersion = true; |
+ buffer.add("${namer.ISOLATE}.makeConstantList"); |
+ buffer.add("(["); |
+ for (int i = 0; i < constant.entries.length; i++) { |
+ if (i != 0) buffer.add(", "); |
+ _visit(constant.entries[i]); |
+ } |
+ buffer.add("])"); |
+ } |
} |
String getJsConstructor(ClassElement element) { |
return namer.isolatePropertiesAccess(element); |
} |
- js.Expression visitMap(MapConstant constant) { |
- js.Expression jsMap() { |
- List<js.Property> properties = <js.Property>[]; |
- int valueIndex = 0; |
- for (int i = 0; i < constant.keys.entries.length; i++) { |
- StringConstant key = constant.keys.entries[i]; |
- if (key.value == MapConstant.PROTO_PROPERTY) continue; |
- |
- // Keys in literal maps must be emitted in place. |
- js.Literal keyExpression = _visit(key); |
- js.Expression valueExpression = |
- _reference(constant.values[valueIndex++]); |
- properties.add(new js.Property(keyExpression, valueExpression)); |
- } |
- if (valueIndex != constant.values.length) { |
- compiler.internalError("Bad value count."); |
+ void visitMap(MapConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ emitCanonicalVersion(constant); |
+ } else { |
+ void writeJsMap() { |
+ buffer.add("{"); |
+ int valueIndex = 0; |
+ for (int i = 0; i < constant.keys.entries.length; i++) { |
+ StringConstant key = constant.keys.entries[i]; |
+ if (key.value == MapConstant.PROTO_PROPERTY) continue; |
+ |
+ if (valueIndex != 0) buffer.add(", "); |
+ |
+ // Keys in literal maps must be emitted in place. |
+ emitJavaScriptCodeForConstant(key, buffer); |
+ |
+ buffer.add(": "); |
+ emitCanonicalVersionOfConstant(constant.values[valueIndex++], buffer); |
+ } |
+ buffer.add("}"); |
+ if (valueIndex != constant.values.length) { |
+ compiler.internalError("Bad value count."); |
+ } |
} |
- return new js.ObjectInitializer(properties); |
- } |
- void badFieldCountError() { |
- compiler.internalError( |
+ void badFieldCountError() { |
+ compiler.internalError( |
"Compiler and ConstantMap disagree on number of fields."); |
- } |
- |
- ClassElement classElement = constant.type.element; |
- |
- List<js.Expression> arguments = <js.Expression>[]; |
- |
- // 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; |
- classElement.implementation.forEachInstanceField( |
- (ClassElement enclosing, Element field) { |
- if (field.name == MapConstant.LENGTH_NAME) { |
- arguments.add( |
- new js.LiteralNumber('${constant.keys.entries.length}')); |
- } else if (field.name == MapConstant.JS_OBJECT_NAME) { |
- arguments.add(jsMap()); |
- } else if (field.name == MapConstant.KEYS_NAME) { |
- arguments.add(_reference(constant.keys)); |
- } else if (field.name == MapConstant.PROTO_VALUE) { |
- assert(constant.protoValue != null); |
- arguments.add(_reference(constant.protoValue)); |
- } else { |
- badFieldCountError(); |
- } |
- emittedArgumentCount++; |
- }, |
- includeBackendMembers: true, |
- includeSuperMembers: true); |
+ } |
- if ((constant.protoValue == null && emittedArgumentCount != 3) || |
- (constant.protoValue != null && emittedArgumentCount != 4)) { |
- badFieldCountError(); |
+ shouldEmitCanonicalVersion = true; |
+ |
+ ClassElement classElement = constant.type.element; |
+ buffer.add("new "); |
+ buffer.add(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; |
+ classElement.implementation.forEachInstanceField( |
+ (ClassElement enclosing, Element field) { |
+ if (emittedArgumentCount != 0) buffer.add(", "); |
+ if (field.name == MapConstant.LENGTH_NAME) { |
+ buffer.add(constant.keys.entries.length); |
+ } else if (field.name == MapConstant.JS_OBJECT_NAME) { |
+ writeJsMap(); |
+ } else if (field.name == MapConstant.KEYS_NAME) { |
+ emitCanonicalVersionOfConstant(constant.keys, buffer); |
+ } else if (field.name == MapConstant.PROTO_VALUE) { |
+ assert(constant.protoValue != null); |
+ emitCanonicalVersionOfConstant(constant.protoValue, buffer); |
+ } else { |
+ badFieldCountError(); |
+ } |
+ emittedArgumentCount++; |
+ }, |
+ includeBackendMembers: true, |
+ includeSuperMembers: true); |
+ if ((constant.protoValue == null && emittedArgumentCount != 3) || |
+ (constant.protoValue != null && emittedArgumentCount != 4)) { |
+ badFieldCountError(); |
+ } |
+ buffer.add(")"); |
} |
- |
- return new js.New( |
- new js.VariableUse(getJsConstructor(classElement)), |
- arguments); |
} |
- js.Expression visitType(TypeConstant constant) { |
- SourceString helperSourceName = const SourceString('createRuntimeType'); |
- Element helper = compiler.findHelper(helperSourceName); |
- JavaScriptBackend backend = compiler.backend; |
- String helperName = backend.namer.getName(helper); |
- DartType type = constant.representedType; |
- Element element = type.element; |
- String typeName; |
- if (type.kind == TypeKind.INTERFACE) { |
- typeName = |
- backend.rti.getStringRepresentation(type, expandRawType: true); |
+ void visitType(TypeConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ emitCanonicalVersion(constant); |
} else { |
- assert(type.kind == TypeKind.TYPEDEF); |
- typeName = element.name.slowToString(); |
+ SourceString helperSourceName = |
+ const SourceString('createRuntimeType'); |
+ Element helper = compiler.findHelper(helperSourceName); |
+ JavaScriptBackend backend = compiler.backend; |
+ String helperName = backend.namer.getName(helper); |
+ DartType type = constant.representedType; |
+ Element element = type.element; |
+ String typeName; |
+ if (type.kind == TypeKind.INTERFACE) { |
+ typeName = |
+ backend.rti.getStringRepresentation(type, expandRawType: true); |
+ } else { |
+ assert(type.kind == TypeKind.TYPEDEF); |
+ typeName = element.name.slowToString(); |
+ } |
+ buffer.add("${namer.CURRENT_ISOLATE}.$helperName('$typeName')"); |
} |
- return new js.Call( |
- new js.PropertyAccess.field( |
- new js.VariableUse(namer.CURRENT_ISOLATE), |
- helperName), |
- [new js.LiteralString("'$typeName'")]); |
- } |
- |
- js.Expression visitConstructed(ConstructedConstant constant) { |
- return new js.New( |
- new js.VariableUse(getJsConstructor(constant.type.element)), |
- _array(constant.fields)); |
} |
- List<js.Expression> _array(List<Constant> values) { |
- List<js.Expression> valueList = <js.Expression>[]; |
- for (int i = 0; i < values.length; i++) { |
- valueList.add(_reference(values[i])); |
+ void visitConstructed(ConstructedConstant constant) { |
+ if (shouldEmitCanonicalVersion) { |
+ emitCanonicalVersion(constant); |
+ } else { |
+ shouldEmitCanonicalVersion = true; |
+ |
+ buffer.add("new "); |
+ buffer.add(getJsConstructor(constant.type.element)); |
+ buffer.add("("); |
+ for (int i = 0; i < constant.fields.length; i++) { |
+ if (i != 0) buffer.add(", "); |
+ _visit(constant.fields[i]); |
+ } |
+ buffer.add(")"); |
} |
- return valueList; |
} |
} |