Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 class ConstantEmitter implements ConstantVisitor { | 7 class ConstantEmitter { |
| 8 final Compiler compiler; | 8 ConstantReferenceEmitter _referenceEmitter; |
| 9 final Namer namer; | 9 ConstantInitializerEmitter _initializerEmitter; |
| 10 | 10 |
| 11 CodeBuffer buffer; | 11 ConstantEmitter(Compiler compiler, Namer namer) { |
| 12 bool shouldEmitCanonicalVersion; | 12 _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); |
| 13 | 13 _initializerEmitter = new ConstantInitializerEmitter( |
| 14 ConstantEmitter(this.compiler, this.namer); | 14 compiler, namer, _referenceEmitter); |
| 15 | |
| 16 /** | |
| 17 * Unless the constant can be emitted multiple times (as for numbers and | |
| 18 * strings) use the canonical name. | |
| 19 */ | |
| 20 void emitCanonicalVersionOfConstant(Constant constant, CodeBuffer newBuffer) { | |
| 21 shouldEmitCanonicalVersion = true; | |
| 22 buffer = newBuffer; | |
| 23 _visit(constant); | |
| 24 } | 15 } |
| 25 | 16 |
| 26 /** | 17 /** |
| 27 * Emit the JavaScript code of the constant. If the constant must be | 18 * Constructs an expression that is a reference to the constant. Uses a |
| 28 * canonicalized this method emits the initialization value. | 19 * canonical name unless the constant can be emitted multiple times (as for |
| 20 * numbers and strings). | |
| 29 */ | 21 */ |
| 30 void emitJavaScriptCodeForConstant(Constant constant, CodeBuffer newBuffer) { | 22 js.Expression reference(Constant constant) { |
| 31 shouldEmitCanonicalVersion = false; | 23 return _referenceEmitter.generate(constant); |
| 32 buffer = newBuffer; | |
| 33 _visit(constant); | |
| 34 } | 24 } |
| 35 | 25 |
| 36 _visit(Constant constant) { | 26 /** |
| 37 constant.accept(this); | 27 * Constructs an expression like [reference], but the expression is valid |
| 28 * during isolate initialization. | |
| 29 */ | |
| 30 js.Expression referenceInInitializationContext(Constant constant) { | |
| 31 return _referenceEmitter.generateInInitializationContext(constant); | |
| 38 } | 32 } |
| 39 | 33 |
| 40 void visitSentinel(SentinelConstant constant) { | 34 /** |
| 41 if (shouldEmitCanonicalVersion) { | 35 * Constructs an expression used to initialize a canonicalized constant. |
| 42 buffer.add(namer.CURRENT_ISOLATE); | 36 */ |
| 37 js.Expression initializationExpression(Constant constant) { | |
| 38 return _initializerEmitter.generate(constant); | |
| 39 } | |
| 40 } | |
| 41 | |
| 42 class ConstantReferenceEmitter implements ConstantVisitor<js.Expression> { | |
|
ngeoffray
2012/11/27 09:26:28
High-level comments on this class please.
sra1
2012/11/28 03:59:44
Done.
| |
| 43 final Compiler compiler; | |
| 44 final Namer namer; | |
| 45 bool inIsolateInitializationContext = false; | |
| 46 | |
| 47 ConstantReferenceEmitter(this.compiler, this.namer); | |
| 48 | |
| 49 js.Expression generate(Constant constant) { | |
| 50 inIsolateInitializationContext = false; | |
| 51 return _visit(constant); | |
| 52 } | |
| 53 | |
| 54 js.Expression generateInInitializationContext(Constant constant) { | |
| 55 inIsolateInitializationContext = true; | |
| 56 return _visit(constant); | |
| 57 } | |
| 58 | |
| 59 js.Expression _visit(Constant constant) { | |
| 60 return constant.accept(this); | |
| 61 } | |
| 62 | |
| 63 js.Expression visitSentinel(SentinelConstant constant) { | |
| 64 return new js.VariableUse(namer.CURRENT_ISOLATE); | |
| 65 } | |
| 66 | |
| 67 js.Expression visitFunction(FunctionConstant constant) { | |
| 68 return inIsolateInitializationContext | |
| 69 ? new js.VariableUse(namer.isolatePropertiesAccess(constant.element)) | |
| 70 : new js.VariableUse(namer.isolateAccess(constant.element)); | |
| 71 } | |
| 72 | |
| 73 js.Expression visitNull(NullConstant constant) { | |
| 74 return new js.LiteralNull(); | |
| 75 } | |
| 76 | |
| 77 js.Expression visitInt(IntConstant constant) { | |
| 78 return new js.LiteralNumber('${constant.value}'); | |
| 79 } | |
| 80 | |
| 81 js.Expression visitDouble(DoubleConstant constant) { | |
| 82 double value = constant.value; | |
| 83 if (value.isNaN) { | |
| 84 return new js.LiteralNumber("(0/0)"); | |
| 85 } else if (value == double.INFINITY) { | |
| 86 return new js.LiteralNumber("(1/0)"); | |
| 87 } else if (value == -double.INFINITY) { | |
| 88 return new js.LiteralNumber("(-1/0)"); | |
| 43 } else { | 89 } else { |
| 44 compiler.internalError( | 90 return new js.LiteralNumber("$value"); |
| 45 "The parameter sentinel constant does not need specific JS code"); | |
| 46 } | 91 } |
| 47 } | 92 } |
| 48 | 93 |
| 49 void visitFunction(FunctionConstant constant) { | 94 js.Expression visitTrue(TrueConstant constant) { |
| 50 if (shouldEmitCanonicalVersion) { | 95 if (compiler.enableMinification) { |
|
ngeoffray
2012/11/27 09:26:28
@erik: Why isn't that being taken care by the mini
sra1
2012/11/28 03:59:44
This transformation is currently done in codegen.
| |
| 51 buffer.add(namer.isolatePropertiesAccess(constant.element)); | 96 // Use !0 for true. |
| 97 return new js.Prefix("!", new js.LiteralNumber("0")); | |
| 52 } else { | 98 } else { |
| 53 compiler.internalError( | 99 return new js.LiteralBool(true); |
| 54 "The function constant does not need specific JS code"); | 100 } |
| 101 | |
| 102 } | |
| 103 | |
| 104 js.Expression visitFalse(FalseConstant constant) { | |
| 105 if (compiler.enableMinification) { | |
| 106 // Use !1 for false. | |
| 107 return new js.Prefix("!", new js.LiteralNumber("1")); | |
| 108 } else { | |
| 109 return new js.LiteralBool(false); | |
| 55 } | 110 } |
| 56 } | 111 } |
| 57 | 112 |
| 58 void visitNull(NullConstant constant) { | |
| 59 buffer.add("null"); | |
| 60 } | |
| 61 | |
| 62 void visitInt(IntConstant constant) { | |
| 63 buffer.add(constant.value.toString()); | |
| 64 } | |
| 65 | |
| 66 void visitDouble(DoubleConstant constant) { | |
| 67 double value = constant.value; | |
| 68 if (value.isNaN) { | |
| 69 buffer.add("(0/0)"); | |
| 70 } else if (value == double.INFINITY) { | |
| 71 buffer.add("(1/0)"); | |
| 72 } else if (value == -double.INFINITY) { | |
| 73 buffer.add("(-1/0)"); | |
| 74 } else { | |
| 75 buffer.add("$value"); | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 void visitTrue(TrueConstant constant) { | |
| 80 buffer.add("true"); | |
| 81 } | |
| 82 | |
| 83 void visitFalse(FalseConstant constant) { | |
| 84 buffer.add("false"); | |
| 85 } | |
| 86 | |
| 87 /** | 113 /** |
| 88 * Write the contents of the quoted string to a [CodeBuffer] in | 114 * Write the contents of the quoted string to a [CodeBuffer] in |
| 89 * a form that is valid as JavaScript string literal content. | 115 * a form that is valid as JavaScript string literal content. |
| 90 * The string is assumed quoted by double quote characters. | 116 * The string is assumed quoted by double quote characters. |
| 91 */ | 117 */ |
| 92 void visitString(StringConstant constant) { | 118 js.Expression visitString(StringConstant constant) { |
| 93 buffer.add('"'); | 119 // TODO(sra): If the string is long *and repeated* (and not on a hot path) |
| 94 writeJsonEscapedCharsOn(constant.value.slowToString(), buffer); | 120 // then it should be assigned to a name. We don't have reference counts (or |
| 95 buffer.add('"'); | 121 // profile information) here, so this is the wrong place. |
| 96 } | 122 StringBuffer sb = new StringBuffer(); |
| 97 | 123 writeJsonEscapedCharsOn(constant.value.slowToString(), sb); |
| 98 void emitCanonicalVersion(Constant constant) { | 124 return new js.LiteralString('"$sb"'); |
| 125 } | |
| 126 | |
| 127 js.Expression emitCanonicalVersion(Constant constant) { | |
| 99 String name = namer.constantName(constant); | 128 String name = namer.constantName(constant); |
| 100 buffer.add(namer.isolatePropertiesAccessForConstant(name)); | 129 if (inIsolateInitializationContext) { |
| 101 } | 130 return namer.isolatePropertiesAccessForConstant(name); |
| 102 | |
| 103 void visitList(ListConstant constant) { | |
| 104 if (shouldEmitCanonicalVersion) { | |
| 105 emitCanonicalVersion(constant); | |
| 106 } else { | 131 } else { |
| 107 shouldEmitCanonicalVersion = true; | 132 js.VariableUse currentIsolateUse = |
| 108 buffer.add("${namer.ISOLATE}.makeConstantList"); | 133 new js.VariableUse(namer.CURRENT_ISOLATE); |
| 109 buffer.add("(["); | 134 return new js.PropertyAccess.field(currentIsolateUse, name); |
| 110 for (int i = 0; i < constant.entries.length; i++) { | 135 } |
| 111 if (i != 0) buffer.add(", "); | 136 } |
| 112 _visit(constant.entries[i]); | 137 |
| 113 } | 138 js.Expression visitList(ListConstant constant) { |
| 114 buffer.add("])"); | 139 return emitCanonicalVersion(constant); |
| 115 } | 140 } |
| 141 | |
| 142 js.Expression visitMap(MapConstant constant) { | |
| 143 return emitCanonicalVersion(constant); | |
| 144 } | |
| 145 | |
| 146 js.Expression visitType(TypeConstant constant) { | |
| 147 return emitCanonicalVersion(constant); | |
| 148 } | |
| 149 | |
| 150 js.Expression visitConstructed(ConstructedConstant constant) { | |
| 151 return emitCanonicalVersion(constant); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 class ConstantInitializerEmitter implements ConstantVisitor<js.Expression> { | |
|
ngeoffray
2012/11/27 09:26:28
Comments too.
sra1
2012/11/28 03:59:44
Done.
| |
| 156 final Compiler compiler; | |
| 157 final Namer namer; | |
| 158 final ConstantReferenceEmitter referenceEmitter; | |
| 159 | |
| 160 ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); | |
| 161 | |
| 162 js.Expression generate(Constant constant) { | |
| 163 return _visit(constant); | |
| 164 } | |
| 165 | |
| 166 js.Expression _visit(Constant constant) { | |
| 167 return constant.accept(this); | |
| 168 } | |
| 169 | |
| 170 js.Expression _reference(Constant constant) { | |
| 171 return referenceEmitter.generateInInitializationContext(constant); | |
| 172 } | |
| 173 | |
| 174 js.Expression visitSentinel(SentinelConstant constant) { | |
| 175 compiler.internalError( | |
| 176 "The parameter sentinel constant does not need specific JS code"); | |
| 177 } | |
| 178 | |
| 179 js.Expression visitFunction(FunctionConstant constant) { | |
| 180 compiler.internalError( | |
| 181 "The function constant does not need specific JS code"); | |
| 182 } | |
| 183 | |
| 184 js.Expression visitNull(NullConstant constant) { | |
| 185 return _reference(constant); | |
| 186 } | |
| 187 | |
| 188 js.Expression visitInt(IntConstant constant) { | |
| 189 return _reference(constant); | |
| 190 } | |
| 191 | |
| 192 js.Expression visitDouble(DoubleConstant constant) { | |
| 193 return _reference(constant); | |
| 194 } | |
| 195 | |
| 196 js.Expression visitTrue(TrueConstant constant) { | |
| 197 return _reference(constant); | |
| 198 } | |
| 199 | |
| 200 js.Expression visitFalse(FalseConstant constant) { | |
| 201 return _reference(constant); | |
| 202 } | |
| 203 | |
| 204 js.Expression visitString(StringConstant constant) { | |
| 205 // TODO(sra): Some larger strings are worth sharing. | |
| 206 return _reference(constant); | |
| 207 } | |
| 208 | |
| 209 js.Expression visitList(ListConstant constant) { | |
| 210 return new js.Call( | |
| 211 new js.PropertyAccess.field( | |
| 212 new js.VariableUse(namer.ISOLATE), | |
| 213 'makeConstantList'), | |
| 214 [new js.ArrayInitializer.from(_array(constant.entries))]); | |
| 116 } | 215 } |
| 117 | 216 |
| 118 String getJsConstructor(ClassElement element) { | 217 String getJsConstructor(ClassElement element) { |
| 119 return namer.isolatePropertiesAccess(element); | 218 return namer.isolatePropertiesAccess(element); |
| 120 } | 219 } |
| 121 | 220 |
| 122 void visitMap(MapConstant constant) { | 221 js.Expression visitMap(MapConstant constant) { |
| 123 if (shouldEmitCanonicalVersion) { | 222 js.Expression jsMap() { |
| 124 emitCanonicalVersion(constant); | 223 List<js.Property> properties = <js.Property>[]; |
| 224 int valueIndex = 0; | |
| 225 for (int i = 0; i < constant.keys.entries.length; i++) { | |
| 226 StringConstant key = constant.keys.entries[i]; | |
| 227 if (key.value == MapConstant.PROTO_PROPERTY) continue; | |
| 228 | |
| 229 // Keys in literal maps must be emitted in place. | |
| 230 js.Literal keyExpression = _visit(key); | |
| 231 js.Expression valueExpression = | |
| 232 _reference(constant.values[valueIndex++]); | |
| 233 properties.add(new js.Property(keyExpression, valueExpression)); | |
| 234 } | |
| 235 if (valueIndex != constant.values.length) { | |
| 236 compiler.internalError("Bad value count."); | |
| 237 } | |
| 238 return new js.ObjectInitializer(properties); | |
| 239 } | |
| 240 | |
| 241 void badFieldCountError() { | |
| 242 compiler.internalError( | |
| 243 "Compiler and ConstantMap disagree on number of fields."); | |
| 244 } | |
| 245 | |
| 246 ClassElement classElement = constant.type.element; | |
| 247 | |
| 248 List<js.Expression> arguments = <js.Expression>[]; | |
| 249 | |
| 250 // The arguments of the JavaScript constructor for any given Dart class | |
| 251 // are in the same order as the members of the class element. | |
| 252 int emittedArgumentCount = 0; | |
| 253 classElement.implementation.forEachInstanceField( | |
| 254 (ClassElement enclosing, Element field) { | |
| 255 if (field.name == MapConstant.LENGTH_NAME) { | |
| 256 arguments.add( | |
| 257 new js.LiteralNumber('${constant.keys.entries.length}')); | |
| 258 } else if (field.name == MapConstant.JS_OBJECT_NAME) { | |
| 259 arguments.add(jsMap()); | |
| 260 } else if (field.name == MapConstant.KEYS_NAME) { | |
| 261 arguments.add(_reference(constant.keys)); | |
| 262 } else if (field.name == MapConstant.PROTO_VALUE) { | |
| 263 assert(constant.protoValue != null); | |
| 264 arguments.add(_reference(constant.protoValue)); | |
| 265 } else { | |
| 266 badFieldCountError(); | |
| 267 } | |
| 268 emittedArgumentCount++; | |
| 269 }, | |
| 270 includeBackendMembers: true, | |
| 271 includeSuperMembers: true); | |
| 272 | |
| 273 if ((constant.protoValue == null && emittedArgumentCount != 3) || | |
| 274 (constant.protoValue != null && emittedArgumentCount != 4)) { | |
| 275 badFieldCountError(); | |
| 276 } | |
| 277 | |
| 278 return new js.New( | |
| 279 new js.VariableUse(getJsConstructor(classElement)), | |
| 280 arguments); | |
| 281 } | |
| 282 | |
| 283 js.Expression visitType(TypeConstant constant) { | |
| 284 SourceString helperSourceName = const SourceString('createRuntimeType'); | |
| 285 Element helper = compiler.findHelper(helperSourceName); | |
| 286 JavaScriptBackend backend = compiler.backend; | |
| 287 String helperName = backend.namer.getName(helper); | |
| 288 DartType type = constant.representedType; | |
| 289 Element element = type.element; | |
| 290 String typeName; | |
| 291 if (type.kind == TypeKind.INTERFACE) { | |
| 292 typeName = | |
| 293 backend.rti.getStringRepresentation(type, expandRawType: true); | |
| 125 } else { | 294 } else { |
| 126 void writeJsMap() { | 295 assert(type.kind == TypeKind.TYPEDEF); |
| 127 buffer.add("{"); | 296 typeName = element.name.slowToString(); |
| 128 int valueIndex = 0; | 297 } |
| 129 for (int i = 0; i < constant.keys.entries.length; i++) { | 298 return new js.Call( |
| 130 StringConstant key = constant.keys.entries[i]; | 299 new js.PropertyAccess.field( |
| 131 if (key.value == MapConstant.PROTO_PROPERTY) continue; | 300 new js.VariableUse(namer.CURRENT_ISOLATE), helperName), |
| 132 | 301 [new js.LiteralString("'$typeName'")]); |
| 133 if (valueIndex != 0) buffer.add(", "); | 302 } |
| 134 | 303 |
| 135 // Keys in literal maps must be emitted in place. | 304 js.Expression visitConstructed(ConstructedConstant constant) { |
| 136 emitJavaScriptCodeForConstant(key, buffer); | 305 return new js.New( |
| 137 | 306 new js.VariableUse(getJsConstructor(constant.type.element)), |
| 138 buffer.add(": "); | 307 _array(constant.fields)); |
| 139 emitCanonicalVersionOfConstant(constant.values[valueIndex++], buffer); | 308 } |
| 140 } | 309 |
| 141 buffer.add("}"); | 310 List<js.Expression> _array(List<Constant> values) { |
| 142 if (valueIndex != constant.values.length) { | 311 List<js.Expression> valueList = <js.Expression>[]; |
| 143 compiler.internalError("Bad value count."); | 312 for (int i = 0; i < values.length; i++) { |
| 144 } | 313 valueList.add(_reference(values[i])); |
| 145 } | 314 } |
| 146 | 315 return valueList; |
| 147 void badFieldCountError() { | |
| 148 compiler.internalError( | |
| 149 "Compiler and ConstantMap disagree on number of fields."); | |
| 150 } | |
| 151 | |
| 152 shouldEmitCanonicalVersion = true; | |
| 153 | |
| 154 ClassElement classElement = constant.type.element; | |
| 155 buffer.add("new "); | |
| 156 buffer.add(getJsConstructor(classElement)); | |
| 157 buffer.add("("); | |
| 158 // The arguments of the JavaScript constructor for any given Dart class | |
| 159 // are in the same order as the members of the class element. | |
| 160 int emittedArgumentCount = 0; | |
| 161 classElement.implementation.forEachInstanceField( | |
| 162 (ClassElement enclosing, Element field) { | |
| 163 if (emittedArgumentCount != 0) buffer.add(", "); | |
| 164 if (field.name == MapConstant.LENGTH_NAME) { | |
| 165 buffer.add(constant.keys.entries.length); | |
| 166 } else if (field.name == MapConstant.JS_OBJECT_NAME) { | |
| 167 writeJsMap(); | |
| 168 } else if (field.name == MapConstant.KEYS_NAME) { | |
| 169 emitCanonicalVersionOfConstant(constant.keys, buffer); | |
| 170 } else if (field.name == MapConstant.PROTO_VALUE) { | |
| 171 assert(constant.protoValue != null); | |
| 172 emitCanonicalVersionOfConstant(constant.protoValue, buffer); | |
| 173 } else { | |
| 174 badFieldCountError(); | |
| 175 } | |
| 176 emittedArgumentCount++; | |
| 177 }, | |
| 178 includeBackendMembers: true, | |
| 179 includeSuperMembers: true); | |
| 180 if ((constant.protoValue == null && emittedArgumentCount != 3) || | |
| 181 (constant.protoValue != null && emittedArgumentCount != 4)) { | |
| 182 badFieldCountError(); | |
| 183 } | |
| 184 buffer.add(")"); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 void visitType(TypeConstant constant) { | |
| 189 if (shouldEmitCanonicalVersion) { | |
| 190 emitCanonicalVersion(constant); | |
| 191 } else { | |
| 192 SourceString helperSourceName = | |
| 193 const SourceString('createRuntimeType'); | |
| 194 Element helper = compiler.findHelper(helperSourceName); | |
| 195 JavaScriptBackend backend = compiler.backend; | |
| 196 String helperName = backend.namer.getName(helper); | |
| 197 DartType type = constant.representedType; | |
| 198 Element element = type.element; | |
| 199 String typeName; | |
| 200 if (type.kind == TypeKind.INTERFACE) { | |
| 201 typeName = | |
| 202 backend.rti.getStringRepresentation(type, expandRawType: true); | |
| 203 } else { | |
| 204 assert(type.kind == TypeKind.TYPEDEF); | |
| 205 typeName = element.name.slowToString(); | |
| 206 } | |
| 207 buffer.add("${namer.CURRENT_ISOLATE}.$helperName('$typeName')"); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 void visitConstructed(ConstructedConstant constant) { | |
| 212 if (shouldEmitCanonicalVersion) { | |
| 213 emitCanonicalVersion(constant); | |
| 214 } else { | |
| 215 shouldEmitCanonicalVersion = true; | |
| 216 | |
| 217 buffer.add("new "); | |
| 218 buffer.add(getJsConstructor(constant.type.element)); | |
| 219 buffer.add("("); | |
| 220 for (int i = 0; i < constant.fields.length; i++) { | |
| 221 if (i != 0) buffer.add(", "); | |
| 222 _visit(constant.fields[i]); | |
| 223 } | |
| 224 buffer.add(")"); | |
| 225 } | |
| 226 } | 316 } |
| 227 } | 317 } |
| OLD | NEW |