| // 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. |
| |
| part of js_backend; |
| |
| class ConstantEmitter { |
| ConstantReferenceEmitter _referenceEmitter; |
| ConstantInitializerEmitter _initializerEmitter; |
| |
| ConstantEmitter(Compiler compiler, Namer namer) { |
| _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); |
| _initializerEmitter = new ConstantInitializerEmitter( |
| compiler, namer, _referenceEmitter); |
| } |
| |
| /** |
| * 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). |
| */ |
| jsAst.Expression reference(Constant constant) { |
| return _referenceEmitter.generate(constant); |
| } |
| |
| /** |
| * Constructs an expression like [reference], but the expression is valid |
| * during isolate initialization. |
| */ |
| jsAst.Expression referenceInInitializationContext(Constant constant) { |
| return _referenceEmitter.generateInInitializationContext(constant); |
| } |
| |
| /** |
| * Constructs an expression used to initialize a canonicalized constant. |
| */ |
| jsAst.Expression initializationExpression(Constant constant) { |
| return _initializerEmitter.generate(constant); |
| } |
| } |
| |
| /** |
| * Visitor for generating JavaScript expressions to refer to [Constant]s. |
| * Do not use directly, use methods from [ConstantEmitter]. |
| */ |
| class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> { |
| final Compiler compiler; |
| final Namer namer; |
| |
| ConstantReferenceEmitter(this.compiler, this.namer); |
| |
| jsAst.Expression generate(Constant constant) { |
| return _visit(constant); |
| } |
| |
| jsAst.Expression generateInInitializationContext(Constant constant) { |
| return _visit(constant); |
| } |
| |
| jsAst.Expression _visit(Constant constant) { |
| return constant.accept(this); |
| } |
| |
| jsAst.Expression visitFunction(FunctionConstant constant) { |
| return new jsAst.VariableUse( |
| namer.isolateStaticClosureAccess(constant.element)); |
| } |
| |
| jsAst.Expression visitNull(NullConstant constant) { |
| return new jsAst.LiteralNull(); |
| } |
| |
| jsAst.Expression visitInt(IntConstant constant) { |
| return new jsAst.LiteralNumber('${constant.value}'); |
| } |
| |
| jsAst.Expression visitDouble(DoubleConstant constant) { |
| double value = constant.value; |
| if (value.isNaN) { |
| return js("0/0"); |
| } else if (value == double.INFINITY) { |
| return js("1/0"); |
| } else if (value == -double.INFINITY) { |
| return js("-1/0"); |
| } else { |
| return new jsAst.LiteralNumber("$value"); |
| } |
| } |
| |
| jsAst.Expression visitTrue(TrueConstant constant) { |
| if (compiler.enableMinification) { |
| // Use !0 for true. |
| return js("!0"); |
| } else { |
| return js('true'); |
| } |
| } |
| |
| jsAst.Expression visitFalse(FalseConstant constant) { |
| if (compiler.enableMinification) { |
| // Use !1 for false. |
| return js("!1"); |
| } else { |
| return js('false'); |
| } |
| } |
| |
| /** |
| * Write the contents of the quoted string to a [CodeBuffer] in |
| * a form that is valid as JavaScript string literal content. |
| * The string is assumed quoted by double quote characters. |
| */ |
| jsAst.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 jsAst.LiteralString('"$sb"'); |
| } |
| |
| jsAst.Expression emitCanonicalVersion(Constant constant) { |
| String name = namer.constantName(constant); |
| return new jsAst.PropertyAccess.field( |
| new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name); |
| } |
| |
| jsAst.Expression visitList(ListConstant constant) { |
| return emitCanonicalVersion(constant); |
| } |
| |
| jsAst.Expression visitMap(MapConstant constant) { |
| return emitCanonicalVersion(constant); |
| } |
| |
| jsAst.Expression visitType(TypeConstant constant) { |
| return emitCanonicalVersion(constant); |
| } |
| |
| jsAst.Expression visitConstructed(ConstructedConstant constant) { |
| return emitCanonicalVersion(constant); |
| } |
| |
| jsAst.Expression visitInterceptor(InterceptorConstant 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<jsAst.Expression> { |
| final Compiler compiler; |
| final Namer namer; |
| final ConstantReferenceEmitter referenceEmitter; |
| |
| ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); |
| |
| jsAst.Expression generate(Constant constant) { |
| return _visit(constant); |
| } |
| |
| jsAst.Expression _visit(Constant constant) { |
| return constant.accept(this); |
| } |
| |
| jsAst.Expression _reference(Constant constant) { |
| return referenceEmitter.generateInInitializationContext(constant); |
| } |
| |
| jsAst.Expression visitFunction(FunctionConstant constant) { |
| compiler.internalError( |
| "The function constant does not need specific JS code"); |
| } |
| |
| jsAst.Expression visitNull(NullConstant constant) { |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitInt(IntConstant constant) { |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitDouble(DoubleConstant constant) { |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitTrue(TrueConstant constant) { |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitFalse(FalseConstant constant) { |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitString(StringConstant constant) { |
| // TODO(sra): Some larger strings are worth sharing. |
| return _reference(constant); |
| } |
| |
| jsAst.Expression visitList(ListConstant constant) { |
| jsAst.Expression value = new jsAst.Call( |
| new jsAst.PropertyAccess.field( |
| new jsAst.VariableUse(namer.isolateName), |
| 'makeConstantList'), |
| [new jsAst.ArrayInitializer.from(_array(constant.entries))]); |
| return maybeAddTypeArguments(constant.type, value); |
| } |
| |
| String getJsConstructor(ClassElement element) { |
| return namer.isolateAccess(element); |
| } |
| |
| jsAst.Expression visitMap(MapConstant constant) { |
| jsAst.Expression jsMap() { |
| List<jsAst.Property> properties = <jsAst.Property>[]; |
| 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. |
| jsAst.Literal keyExpression = _visit(key); |
| jsAst.Expression valueExpression = |
| _reference(constant.values[i]); |
| properties.add(new jsAst.Property(keyExpression, valueExpression)); |
| } |
| return new jsAst.ObjectInitializer(properties); |
| } |
| |
| jsAst.Expression jsGeneralMap() { |
| List<jsAst.Expression> data = <jsAst.Expression>[]; |
| for (int i = 0; i < constant.keys.entries.length; i++) { |
| jsAst.Expression keyExpression = |
| _reference(constant.keys.entries[i]); |
| jsAst.Expression valueExpression = |
| _reference(constant.values[i]); |
| data.add(keyExpression); |
| data.add(valueExpression); |
| } |
| return new jsAst.ArrayInitializer.from(data); |
| } |
| |
| ClassElement classElement = constant.type.element; |
| SourceString className = classElement.name; |
| |
| List<jsAst.Expression> arguments = <jsAst.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 jsAst.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 if (field.name == MapConstant.JS_DATA_NAME) { |
| arguments.add(jsGeneralMap()); |
| } else { |
| compiler.internalError( |
| "Compiler has unexpected field ${field.name} for " |
| "${className}."); |
| } |
| emittedArgumentCount++; |
| }, |
| includeSuperAndInjectedMembers: true); |
| if ((className == MapConstant.DART_STRING_CLASS && |
| emittedArgumentCount != 3) || |
| (className == MapConstant.DART_PROTO_CLASS && |
| emittedArgumentCount != 4) || |
| (className == MapConstant.DART_GENERAL_CLASS && |
| emittedArgumentCount != 1)) { |
| compiler.internalError( |
| "Compiler and ${className} disagree on number of fields."); |
| } |
| |
| jsAst.Expression value = new jsAst.New( |
| new jsAst.VariableUse(getJsConstructor(classElement)), |
| arguments); |
| return maybeAddTypeArguments(constant.type, value); |
| } |
| |
| JavaScriptBackend get backend => compiler.backend; |
| |
| jsAst.PropertyAccess getHelperProperty(Element helper) { |
| return backend.namer.elementAccess(helper); |
| } |
| |
| jsAst.Expression visitType(TypeConstant constant) { |
| DartType type = constant.representedType; |
| String name = namer.getRuntimeTypeName(type.element); |
| jsAst.Expression typeName = new jsAst.LiteralString("'$name'"); |
| return new jsAst.Call(getHelperProperty(backend.getCreateRuntimeType()), |
| [typeName]); |
| } |
| |
| jsAst.Expression visitInterceptor(InterceptorConstant constant) { |
| return new jsAst.PropertyAccess.field( |
| new jsAst.VariableUse( |
| getJsConstructor(constant.dispatchedType.element)), |
| 'prototype'); |
| } |
| |
| jsAst.Expression visitConstructed(ConstructedConstant constant) { |
| Element element = constant.type.element; |
| if (element.isForeign(compiler) |
| && element.name == const SourceString('JS_CONST')) { |
| StringConstant str = constant.fields[0]; |
| return new jsAst.LiteralExpression(str.value.slowToString()); |
| } |
| jsAst.New instantiation = new jsAst.New( |
| new jsAst.VariableUse(getJsConstructor(constant.type.element)), |
| _array(constant.fields)); |
| return maybeAddTypeArguments(constant.type, instantiation); |
| } |
| |
| List<jsAst.Expression> _array(List<Constant> values) { |
| List<jsAst.Expression> valueList = <jsAst.Expression>[]; |
| for (int i = 0; i < values.length; i++) { |
| valueList.add(_reference(values[i])); |
| } |
| return valueList; |
| } |
| |
| jsAst.Expression maybeAddTypeArguments(InterfaceType type, |
| jsAst.Expression value) { |
| if (type is InterfaceType && |
| !type.isRaw && |
| backend.classNeedsRti(type.element)) { |
| InterfaceType interface = type; |
| RuntimeTypes rti = backend.rti; |
| Iterable<String> arguments = interface.typeArguments |
| .toList(growable: false) |
| .map((DartType type) => rti.getTypeRepresentation(type, (_){})); |
| jsAst.Expression argumentList = |
| new jsAst.LiteralString('[${arguments.join(', ')}]'); |
| return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), |
| [value, argumentList]); |
| } |
| return value; |
| } |
| } |