| 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 { | 7 typedef jsAst.Expression _ConstantReferenceGenerator(ConstantValue constant); |
| 8 ConstantReferenceEmitter _referenceEmitter; | |
| 9 ConstantLiteralEmitter _literalEmitter; | |
| 10 | |
| 11 ConstantEmitter(Compiler compiler, | |
| 12 Namer namer, | |
| 13 jsAst.Template makeConstantListTemplate) { | |
| 14 _literalEmitter = new ConstantLiteralEmitter( | |
| 15 compiler, namer, makeConstantListTemplate, this); | |
| 16 _referenceEmitter = new ConstantReferenceEmitter(compiler, namer, this); | |
| 17 } | |
| 18 | |
| 19 /** | |
| 20 * Constructs an expression that is a reference to the constant. Uses a | |
| 21 * canonical name unless the constant can be emitted multiple times (as for | |
| 22 * numbers and strings). | |
| 23 */ | |
| 24 jsAst.Expression reference(ConstantValue constant) { | |
| 25 return _referenceEmitter.generate(constant); | |
| 26 } | |
| 27 | |
| 28 /** | |
| 29 * Constructs a literal expression that evaluates to the constant. Uses a | |
| 30 * canonical name unless the constant can be emitted multiple times (as for | |
| 31 * numbers and strings). | |
| 32 */ | |
| 33 jsAst.Expression literal(ConstantValue constant) { | |
| 34 return _literalEmitter.generate(constant); | |
| 35 } | |
| 36 | |
| 37 /** | |
| 38 * Constructs an expression like [reference], but the expression is valid | |
| 39 * during isolate initialization. | |
| 40 */ | |
| 41 jsAst.Expression referenceInInitializationContext(ConstantValue constant) { | |
| 42 return _referenceEmitter.generate(constant); | |
| 43 } | |
| 44 | |
| 45 /** | |
| 46 * Constructs an expression used to initialize a canonicalized constant. | |
| 47 */ | |
| 48 jsAst.Expression initializationExpression(ConstantValue constant) { | |
| 49 return _literalEmitter.generate(constant); | |
| 50 } | |
| 51 } | |
| 52 | 8 |
| 53 /** | 9 /** |
| 54 * Visitor for generating JavaScript expressions to refer to [ConstantValue]s. | 10 * Generates the JavaScript expressions for constants. |
| 55 * Do not use directly, use methods from [ConstantEmitter]. | 11 * |
| 12 * It uses a given [constantReferenceGenerator] to reference nested constants |
| 13 * (if there are some). It is hence up to that function to decide which |
| 14 * constants should be inlined or not. |
| 56 */ | 15 */ |
| 57 class ConstantReferenceEmitter | 16 class ConstantEmitter |
| 58 implements ConstantValueVisitor<jsAst.Expression, Null> { | |
| 59 final Compiler compiler; | |
| 60 final Namer namer; | |
| 61 | |
| 62 final ConstantEmitter constantEmitter; | |
| 63 | |
| 64 ConstantReferenceEmitter(this.compiler, this.namer, this.constantEmitter); | |
| 65 | |
| 66 JavaScriptBackend get backend => compiler.backend; | |
| 67 | |
| 68 jsAst.Expression generate(ConstantValue constant) { | |
| 69 return _visit(constant); | |
| 70 } | |
| 71 | |
| 72 jsAst.Expression _visit(ConstantValue constant) { | |
| 73 return constant.accept(this, null); | |
| 74 } | |
| 75 | |
| 76 jsAst.Expression emitCanonicalVersion(ConstantValue constant) { | |
| 77 String name = namer.constantName(constant); | |
| 78 return new jsAst.PropertyAccess.field( | |
| 79 new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name); | |
| 80 } | |
| 81 | |
| 82 jsAst.Expression literal(ConstantValue constant) { | |
| 83 return constantEmitter.literal(constant); | |
| 84 } | |
| 85 | |
| 86 @override | |
| 87 jsAst.Expression visitFunction(FunctionConstantValue constant, [_]) { | |
| 88 return backend.emitter.isolateStaticClosureAccess(constant.element); | |
| 89 } | |
| 90 | |
| 91 @override | |
| 92 jsAst.Expression visitNull(NullConstantValue constant, [_]) { | |
| 93 return literal(constant); | |
| 94 } | |
| 95 | |
| 96 @override | |
| 97 jsAst.Expression visitInt(IntConstantValue constant, [_]) { | |
| 98 return literal(constant); | |
| 99 } | |
| 100 | |
| 101 @override | |
| 102 jsAst.Expression visitDouble(DoubleConstantValue constant, [_]) { | |
| 103 return literal(constant); | |
| 104 } | |
| 105 | |
| 106 @override | |
| 107 jsAst.Expression visitBool(BoolConstantValue constant, [_]) { | |
| 108 return literal(constant); | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * Write the contents of the quoted string to a [CodeBuffer] in | |
| 113 * a form that is valid as JavaScript string literal content. | |
| 114 * The string is assumed quoted by double quote characters. | |
| 115 */ | |
| 116 @override | |
| 117 jsAst.Expression visitString(StringConstantValue constant, [_]) { | |
| 118 // TODO(sra): If the string is long *and repeated* (and not on a hot path) | |
| 119 // then it should be assigned to a name. We don't have reference counts (or | |
| 120 // profile information) here, so this is the wrong place. | |
| 121 return literal(constant); | |
| 122 } | |
| 123 | |
| 124 @override | |
| 125 jsAst.Expression visitList(ListConstantValue constant, [_]) { | |
| 126 return emitCanonicalVersion(constant); | |
| 127 } | |
| 128 | |
| 129 @override | |
| 130 jsAst.Expression visitMap(MapConstantValue constant, [_]) { | |
| 131 return emitCanonicalVersion(constant); | |
| 132 } | |
| 133 | |
| 134 @override | |
| 135 jsAst.Expression visitType(TypeConstantValue constant, [_]) { | |
| 136 return emitCanonicalVersion(constant); | |
| 137 } | |
| 138 | |
| 139 @override | |
| 140 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { | |
| 141 return emitCanonicalVersion(constant); | |
| 142 } | |
| 143 | |
| 144 @override | |
| 145 jsAst.Expression visitInterceptor(InterceptorConstantValue constant, [_]) { | |
| 146 return emitCanonicalVersion(constant); | |
| 147 } | |
| 148 | |
| 149 @override | |
| 150 jsAst.Expression visitDummy(DummyConstantValue constant, [_]) { | |
| 151 return literal(constant); | |
| 152 } | |
| 153 | |
| 154 @override | |
| 155 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { | |
| 156 return emitCanonicalVersion(constant); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 * Visitor for generating JavaScript expressions that litterally represent | |
| 162 * [ConstantValue]s. These can be used for inlining constants or in | |
| 163 * initializers. Do not use directly, use methods from [ConstantEmitter]. | |
| 164 */ | |
| 165 class ConstantLiteralEmitter | |
| 166 implements ConstantValueVisitor<jsAst.Expression, Null> { | 17 implements ConstantValueVisitor<jsAst.Expression, Null> { |
| 167 | 18 |
| 168 // Matches blank lines, comment lines and trailing comments that can't be part | 19 // Matches blank lines, comment lines and trailing comments that can't be part |
| 169 // of a string. | 20 // of a string. |
| 170 static final RegExp COMMENT_RE = | 21 static final RegExp COMMENT_RE = |
| 171 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); | 22 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); |
| 172 | 23 |
| 173 final Compiler compiler; | 24 final Compiler compiler; |
| 174 final Namer namer; | 25 final Namer namer; |
| 26 final _ConstantReferenceGenerator constantReferenceGenerator; |
| 175 final jsAst.Template makeConstantListTemplate; | 27 final jsAst.Template makeConstantListTemplate; |
| 176 final ConstantEmitter constantEmitter; | |
| 177 | 28 |
| 178 ConstantLiteralEmitter(this.compiler, | 29 /** |
| 179 this.namer, | 30 * The given [constantReferenceGenerator] function must, when invoked with a |
| 180 this.makeConstantListTemplate, | 31 * constant, either return a reference or return its literal expression if it |
| 181 this.constantEmitter); | 32 * can be inlined. |
| 33 */ |
| 34 ConstantEmitter( |
| 35 this.compiler, |
| 36 this.namer, |
| 37 jsAst.Expression this.constantReferenceGenerator(ConstantValue constant), |
| 38 this.makeConstantListTemplate); |
| 182 | 39 |
| 40 /** |
| 41 * Constructs a literal expression that evaluates to the constant. Uses a |
| 42 * canonical name unless the constant can be emitted multiple times (as for |
| 43 * numbers and strings). |
| 44 */ |
| 183 jsAst.Expression generate(ConstantValue constant) { | 45 jsAst.Expression generate(ConstantValue constant) { |
| 184 return _visit(constant); | 46 return _visit(constant); |
| 185 } | 47 } |
| 186 | 48 |
| 187 jsAst.Expression _visit(ConstantValue constant) { | 49 jsAst.Expression _visit(ConstantValue constant) { |
| 188 return constant.accept(this, null); | 50 return constant.accept(this, null); |
| 189 } | 51 } |
| 190 | 52 |
| 191 @override | 53 @override |
| 192 jsAst.Expression visitFunction(FunctionConstantValue constant, [_]) { | 54 jsAst.Expression visitFunction(FunctionConstantValue constant, [_]) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 287 */ | 149 */ |
| 288 @override | 150 @override |
| 289 jsAst.Expression visitString(StringConstantValue constant, [_]) { | 151 jsAst.Expression visitString(StringConstantValue constant, [_]) { |
| 290 StringBuffer sb = new StringBuffer(); | 152 StringBuffer sb = new StringBuffer(); |
| 291 writeJsonEscapedCharsOn(constant.primitiveValue.slowToString(), sb); | 153 writeJsonEscapedCharsOn(constant.primitiveValue.slowToString(), sb); |
| 292 return new jsAst.LiteralString('"$sb"'); | 154 return new jsAst.LiteralString('"$sb"'); |
| 293 } | 155 } |
| 294 | 156 |
| 295 @override | 157 @override |
| 296 jsAst.Expression visitList(ListConstantValue constant, [_]) { | 158 jsAst.Expression visitList(ListConstantValue constant, [_]) { |
| 297 List<jsAst.Expression> elements = _array(constant.entries); | 159 List<jsAst.Expression> elements = constant.entries |
| 160 .map(constantReferenceGenerator) |
| 161 .toList(growable: false); |
| 298 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); | 162 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); |
| 299 jsAst.Expression value = makeConstantListTemplate.instantiate([array]); | 163 jsAst.Expression value = makeConstantListTemplate.instantiate([array]); |
| 300 return maybeAddTypeArguments(constant.type, value); | 164 return maybeAddTypeArguments(constant.type, value); |
| 301 } | 165 } |
| 302 | 166 |
| 303 @override | 167 @override |
| 304 jsAst.Expression visitMap(JavaScriptMapConstant constant, [_]) { | 168 jsAst.Expression visitMap(JavaScriptMapConstant constant, [_]) { |
| 305 jsAst.Expression jsMap() { | 169 jsAst.Expression jsMap() { |
| 306 List<jsAst.Property> properties = <jsAst.Property>[]; | 170 List<jsAst.Property> properties = <jsAst.Property>[]; |
| 307 for (int i = 0; i < constant.length; i++) { | 171 for (int i = 0; i < constant.length; i++) { |
| 308 StringConstantValue key = constant.keys[i]; | 172 StringConstantValue key = constant.keys[i]; |
| 309 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | 173 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { |
| 310 continue; | 174 continue; |
| 311 } | 175 } |
| 312 | 176 |
| 313 // Keys in literal maps must be emitted in place. | 177 // Keys in literal maps must be emitted in place. |
| 314 jsAst.Literal keyExpression = _visit(key); | 178 jsAst.Literal keyExpression = _visit(key); |
| 315 jsAst.Expression valueExpression = | 179 jsAst.Expression valueExpression = |
| 316 constantEmitter.reference(constant.values[i]); | 180 constantReferenceGenerator(constant.values[i]); |
| 317 properties.add(new jsAst.Property(keyExpression, valueExpression)); | 181 properties.add(new jsAst.Property(keyExpression, valueExpression)); |
| 318 } | 182 } |
| 319 return new jsAst.ObjectInitializer(properties); | 183 return new jsAst.ObjectInitializer(properties); |
| 320 } | 184 } |
| 321 | 185 |
| 322 jsAst.Expression jsGeneralMap() { | 186 jsAst.Expression jsGeneralMap() { |
| 323 List<jsAst.Expression> data = <jsAst.Expression>[]; | 187 List<jsAst.Expression> data = <jsAst.Expression>[]; |
| 324 for (int i = 0; i < constant.keys.length; i++) { | 188 for (int i = 0; i < constant.keys.length; i++) { |
| 325 jsAst.Expression keyExpression = | 189 jsAst.Expression keyExpression = constantReferenceGenerator(constant.key
s[i]); |
| 326 constantEmitter.reference(constant.keys[i]); | |
| 327 jsAst.Expression valueExpression = | 190 jsAst.Expression valueExpression = |
| 328 constantEmitter.reference(constant.values[i]); | 191 constantReferenceGenerator(constant.values[i]); |
| 329 data.add(keyExpression); | 192 data.add(keyExpression); |
| 330 data.add(valueExpression); | 193 data.add(valueExpression); |
| 331 } | 194 } |
| 332 return new jsAst.ArrayInitializer(data); | 195 return new jsAst.ArrayInitializer(data); |
| 333 } | 196 } |
| 334 | 197 |
| 335 ClassElement classElement = constant.type.element; | 198 ClassElement classElement = constant.type.element; |
| 336 String className = classElement.name; | 199 String className = classElement.name; |
| 337 | 200 |
| 338 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 201 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 339 | 202 |
| 340 // The arguments of the JavaScript constructor for any given Dart class | 203 // The arguments of the JavaScript constructor for any given Dart class |
| 341 // are in the same order as the members of the class element. | 204 // are in the same order as the members of the class element. |
| 342 int emittedArgumentCount = 0; | 205 int emittedArgumentCount = 0; |
| 343 classElement.implementation.forEachInstanceField( | 206 classElement.implementation.forEachInstanceField( |
| 344 (ClassElement enclosing, Element field) { | 207 (ClassElement enclosing, Element field) { |
| 345 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { | 208 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { |
| 346 arguments.add( | 209 arguments.add( |
| 347 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); | 210 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); |
| 348 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { | 211 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { |
| 349 arguments.add(jsMap()); | 212 arguments.add(jsMap()); |
| 350 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { | 213 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { |
| 351 arguments.add(constantEmitter.reference(constant.keyList)); | 214 arguments.add(constantReferenceGenerator(constant.keyList)); |
| 352 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { | 215 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { |
| 353 assert(constant.protoValue != null); | 216 assert(constant.protoValue != null); |
| 354 arguments.add(constantEmitter.reference(constant.protoValue)); | 217 arguments.add(constantReferenceGenerator(constant.protoValue)); |
| 355 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { | 218 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { |
| 356 arguments.add(jsGeneralMap()); | 219 arguments.add(jsGeneralMap()); |
| 357 } else { | 220 } else { |
| 358 compiler.internalError(field, | 221 compiler.internalError(field, |
| 359 "Compiler has unexpected field ${field.name} for " | 222 "Compiler has unexpected field ${field.name} for " |
| 360 "${className}."); | 223 "${className}."); |
| 361 } | 224 } |
| 362 emittedArgumentCount++; | 225 emittedArgumentCount++; |
| 363 }, | 226 }, |
| 364 includeSuperAndInjectedMembers: true); | 227 includeSuperAndInjectedMembers: true); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 408 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { | 271 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { |
| 409 Element element = constant.type.element; | 272 Element element = constant.type.element; |
| 410 if (element.isForeign(backend) | 273 if (element.isForeign(backend) |
| 411 && element.name == 'JS_CONST') { | 274 && element.name == 'JS_CONST') { |
| 412 StringConstantValue str = constant.fields[0]; | 275 StringConstantValue str = constant.fields[0]; |
| 413 String value = str.primitiveValue.slowToString(); | 276 String value = str.primitiveValue.slowToString(); |
| 414 return new jsAst.LiteralExpression(stripComments(value)); | 277 return new jsAst.LiteralExpression(stripComments(value)); |
| 415 } | 278 } |
| 416 jsAst.Expression constructor = | 279 jsAst.Expression constructor = |
| 417 backend.emitter.constructorAccess(constant.type.element); | 280 backend.emitter.constructorAccess(constant.type.element); |
| 418 jsAst.New instantiation = | 281 List<jsAst.Expression> fields = |
| 419 new jsAst.New(constructor, _array(constant.fields)); | 282 constant.fields.map(constantReferenceGenerator).toList(growable: false); |
| 283 jsAst.New instantiation = new jsAst.New(constructor, fields); |
| 420 return maybeAddTypeArguments(constant.type, instantiation); | 284 return maybeAddTypeArguments(constant.type, instantiation); |
| 421 } | 285 } |
| 422 | 286 |
| 423 String stripComments(String rawJavaScript) { | 287 String stripComments(String rawJavaScript) { |
| 424 return rawJavaScript.replaceAll(COMMENT_RE, ''); | 288 return rawJavaScript.replaceAll(COMMENT_RE, ''); |
| 425 } | 289 } |
| 426 | 290 |
| 427 List<jsAst.Expression> _array(List<ConstantValue> values) { | |
| 428 return values.map(constantEmitter.reference).toList(growable: false); | |
| 429 } | |
| 430 | |
| 431 jsAst.Expression maybeAddTypeArguments(InterfaceType type, | 291 jsAst.Expression maybeAddTypeArguments(InterfaceType type, |
| 432 jsAst.Expression value) { | 292 jsAst.Expression value) { |
| 433 if (type is InterfaceType && | 293 if (type is InterfaceType && |
| 434 !type.treatAsRaw && | 294 !type.treatAsRaw && |
| 435 backend.classNeedsRti(type.element)) { | 295 backend.classNeedsRti(type.element)) { |
| 436 InterfaceType interface = type; | 296 InterfaceType interface = type; |
| 437 RuntimeTypes rti = backend.rti; | 297 RuntimeTypes rti = backend.rti; |
| 438 Iterable<String> arguments = interface.typeArguments | 298 Iterable<String> arguments = interface.typeArguments |
| 439 .map((DartType type) => | 299 .map((DartType type) => |
| 440 rti.getTypeRepresentationWithHashes(type, (_){})); | 300 rti.getTypeRepresentationWithHashes(type, (_){})); |
| 441 jsAst.Expression argumentList = | 301 jsAst.Expression argumentList = |
| 442 new jsAst.LiteralString('[${arguments.join(', ')}]'); | 302 new jsAst.LiteralString('[${arguments.join(', ')}]'); |
| 443 return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), | 303 return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), |
| 444 [value, argumentList]); | 304 [value, argumentList]); |
| 445 } | 305 } |
| 446 return value; | 306 return value; |
| 447 } | 307 } |
| 448 | 308 |
| 449 @override | 309 @override |
| 450 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { | 310 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { |
| 451 return constantEmitter.reference(constant.referenced); | 311 return constantReferenceGenerator(constant.referenced); |
| 452 } | 312 } |
| 453 } | 313 } |
| OLD | NEW |