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 { | |
| 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 | |
| 53 /** | 7 /** |
| 54 * Visitor for generating JavaScript expressions to refer to [ConstantValue]s. | 8 * Generates the JavaScript expressions for constants. |
| 55 * Do not use directly, use methods from [ConstantEmitter]. | 9 * |
| 10 * It uses a given [constantReference] to reference nested constants (if there | |
| 11 * are some). It is hence up to that function to decide which constants should | |
| 12 * be inlined or not. | |
| 56 */ | 13 */ |
| 57 class ConstantReferenceEmitter | 14 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> { | 15 implements ConstantValueVisitor<jsAst.Expression, Null> { |
| 167 | 16 |
| 168 // Matches blank lines, comment lines and trailing comments that can't be part | 17 // Matches blank lines, comment lines and trailing comments that can't be part |
| 169 // of a string. | 18 // of a string. |
| 170 static final RegExp COMMENT_RE = | 19 static final RegExp COMMENT_RE = |
| 171 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); | 20 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); |
| 172 | 21 |
| 173 final Compiler compiler; | 22 final Compiler compiler; |
| 174 final Namer namer; | 23 final Namer namer; |
| 24 final Function constantReference; | |
|
floitsch
2015/02/24 23:08:48
I know the function isn't typed, but I really hate
Johnni Winther
2015/02/25 11:37:02
Make a typedef like
typedef jsAst.Expression Cons
floitsch
2015/02/25 16:35:18
Done.
| |
| 175 final jsAst.Template makeConstantListTemplate; | 25 final jsAst.Template makeConstantListTemplate; |
| 176 final ConstantEmitter constantEmitter; | |
| 177 | 26 |
| 178 ConstantLiteralEmitter(this.compiler, | 27 /** |
| 179 this.namer, | 28 * The given [constantReferenc] function must, when invoked with a constant, |
|
Johnni Winther
2015/02/25 11:37:02
[constantReferenc] -> [constantReference] (if not
floitsch
2015/02/25 16:35:18
Done.
| |
| 180 this.makeConstantListTemplate, | 29 * either return a reference or return its literal expression if it can |
| 181 this.constantEmitter); | 30 * be inlined. |
| 31 */ | |
| 32 ConstantEmitter( | |
| 33 this.compiler, | |
| 34 this.namer, | |
| 35 jsAst.Expression this.constantReference(ConstantValue constant), | |
| 36 this.makeConstantListTemplate); | |
| 182 | 37 |
| 38 /** | |
| 39 * Constructs a literal expression that evaluates to the constant. Uses a | |
| 40 * canonical name unless the constant can be emitted multiple times (as for | |
| 41 * numbers and strings). | |
| 42 */ | |
| 183 jsAst.Expression generate(ConstantValue constant) { | 43 jsAst.Expression generate(ConstantValue constant) { |
| 184 return _visit(constant); | 44 return _visit(constant); |
| 185 } | 45 } |
| 186 | 46 |
| 187 jsAst.Expression _visit(ConstantValue constant) { | 47 jsAst.Expression _visit(ConstantValue constant) { |
| 188 return constant.accept(this, null); | 48 return constant.accept(this, null); |
| 189 } | 49 } |
| 190 | 50 |
| 191 @override | 51 @override |
| 192 jsAst.Expression visitFunction(FunctionConstantValue constant, [_]) { | 52 jsAst.Expression visitFunction(FunctionConstantValue constant, [_]) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 287 */ | 147 */ |
| 288 @override | 148 @override |
| 289 jsAst.Expression visitString(StringConstantValue constant, [_]) { | 149 jsAst.Expression visitString(StringConstantValue constant, [_]) { |
| 290 StringBuffer sb = new StringBuffer(); | 150 StringBuffer sb = new StringBuffer(); |
| 291 writeJsonEscapedCharsOn(constant.primitiveValue.slowToString(), sb); | 151 writeJsonEscapedCharsOn(constant.primitiveValue.slowToString(), sb); |
| 292 return new jsAst.LiteralString('"$sb"'); | 152 return new jsAst.LiteralString('"$sb"'); |
| 293 } | 153 } |
| 294 | 154 |
| 295 @override | 155 @override |
| 296 jsAst.Expression visitList(ListConstantValue constant, [_]) { | 156 jsAst.Expression visitList(ListConstantValue constant, [_]) { |
| 297 List<jsAst.Expression> elements = _array(constant.entries); | 157 List<jsAst.Expression> elements = |
|
floitsch
2015/02/24 23:08:48
I initially thought that I could get away with an
| |
| 158 constant.entries.map(constantReference).toList(growable: false); | |
| 298 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); | 159 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); |
| 299 jsAst.Expression value = makeConstantListTemplate.instantiate([array]); | 160 jsAst.Expression value = makeConstantListTemplate.instantiate([array]); |
| 300 return maybeAddTypeArguments(constant.type, value); | 161 return maybeAddTypeArguments(constant.type, value); |
| 301 } | 162 } |
| 302 | 163 |
| 303 @override | 164 @override |
| 304 jsAst.Expression visitMap(JavaScriptMapConstant constant, [_]) { | 165 jsAst.Expression visitMap(JavaScriptMapConstant constant, [_]) { |
| 305 jsAst.Expression jsMap() { | 166 jsAst.Expression jsMap() { |
| 306 List<jsAst.Property> properties = <jsAst.Property>[]; | 167 List<jsAst.Property> properties = <jsAst.Property>[]; |
| 307 for (int i = 0; i < constant.length; i++) { | 168 for (int i = 0; i < constant.length; i++) { |
| 308 StringConstantValue key = constant.keys[i]; | 169 StringConstantValue key = constant.keys[i]; |
| 309 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | 170 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { |
| 310 continue; | 171 continue; |
| 311 } | 172 } |
| 312 | 173 |
| 313 // Keys in literal maps must be emitted in place. | 174 // Keys in literal maps must be emitted in place. |
| 314 jsAst.Literal keyExpression = _visit(key); | 175 jsAst.Literal keyExpression = _visit(key); |
| 315 jsAst.Expression valueExpression = | 176 jsAst.Expression valueExpression = |
| 316 constantEmitter.reference(constant.values[i]); | 177 constantReference(constant.values[i]); |
| 317 properties.add(new jsAst.Property(keyExpression, valueExpression)); | 178 properties.add(new jsAst.Property(keyExpression, valueExpression)); |
| 318 } | 179 } |
| 319 return new jsAst.ObjectInitializer(properties); | 180 return new jsAst.ObjectInitializer(properties); |
| 320 } | 181 } |
| 321 | 182 |
| 322 jsAst.Expression jsGeneralMap() { | 183 jsAst.Expression jsGeneralMap() { |
| 323 List<jsAst.Expression> data = <jsAst.Expression>[]; | 184 List<jsAst.Expression> data = <jsAst.Expression>[]; |
| 324 for (int i = 0; i < constant.keys.length; i++) { | 185 for (int i = 0; i < constant.keys.length; i++) { |
| 325 jsAst.Expression keyExpression = | 186 jsAst.Expression keyExpression = constantReference(constant.keys[i]); |
| 326 constantEmitter.reference(constant.keys[i]); | |
| 327 jsAst.Expression valueExpression = | 187 jsAst.Expression valueExpression = |
| 328 constantEmitter.reference(constant.values[i]); | 188 constantReference(constant.values[i]); |
| 329 data.add(keyExpression); | 189 data.add(keyExpression); |
| 330 data.add(valueExpression); | 190 data.add(valueExpression); |
| 331 } | 191 } |
| 332 return new jsAst.ArrayInitializer(data); | 192 return new jsAst.ArrayInitializer(data); |
| 333 } | 193 } |
| 334 | 194 |
| 335 ClassElement classElement = constant.type.element; | 195 ClassElement classElement = constant.type.element; |
| 336 String className = classElement.name; | 196 String className = classElement.name; |
| 337 | 197 |
| 338 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 198 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 339 | 199 |
| 340 // The arguments of the JavaScript constructor for any given Dart class | 200 // The arguments of the JavaScript constructor for any given Dart class |
| 341 // are in the same order as the members of the class element. | 201 // are in the same order as the members of the class element. |
| 342 int emittedArgumentCount = 0; | 202 int emittedArgumentCount = 0; |
| 343 classElement.implementation.forEachInstanceField( | 203 classElement.implementation.forEachInstanceField( |
| 344 (ClassElement enclosing, Element field) { | 204 (ClassElement enclosing, Element field) { |
| 345 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { | 205 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { |
| 346 arguments.add( | 206 arguments.add( |
| 347 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); | 207 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); |
| 348 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { | 208 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { |
| 349 arguments.add(jsMap()); | 209 arguments.add(jsMap()); |
| 350 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { | 210 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { |
| 351 arguments.add(constantEmitter.reference(constant.keyList)); | 211 arguments.add(constantReference(constant.keyList)); |
| 352 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { | 212 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { |
| 353 assert(constant.protoValue != null); | 213 assert(constant.protoValue != null); |
| 354 arguments.add(constantEmitter.reference(constant.protoValue)); | 214 arguments.add(constantReference(constant.protoValue)); |
| 355 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { | 215 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { |
| 356 arguments.add(jsGeneralMap()); | 216 arguments.add(jsGeneralMap()); |
| 357 } else { | 217 } else { |
| 358 compiler.internalError(field, | 218 compiler.internalError(field, |
| 359 "Compiler has unexpected field ${field.name} for " | 219 "Compiler has unexpected field ${field.name} for " |
| 360 "${className}."); | 220 "${className}."); |
| 361 } | 221 } |
| 362 emittedArgumentCount++; | 222 emittedArgumentCount++; |
| 363 }, | 223 }, |
| 364 includeSuperAndInjectedMembers: true); | 224 includeSuperAndInjectedMembers: true); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 408 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { | 268 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { |
| 409 Element element = constant.type.element; | 269 Element element = constant.type.element; |
| 410 if (element.isForeign(backend) | 270 if (element.isForeign(backend) |
| 411 && element.name == 'JS_CONST') { | 271 && element.name == 'JS_CONST') { |
| 412 StringConstantValue str = constant.fields[0]; | 272 StringConstantValue str = constant.fields[0]; |
| 413 String value = str.primitiveValue.slowToString(); | 273 String value = str.primitiveValue.slowToString(); |
| 414 return new jsAst.LiteralExpression(stripComments(value)); | 274 return new jsAst.LiteralExpression(stripComments(value)); |
| 415 } | 275 } |
| 416 jsAst.Expression constructor = | 276 jsAst.Expression constructor = |
| 417 backend.emitter.constructorAccess(constant.type.element); | 277 backend.emitter.constructorAccess(constant.type.element); |
| 418 jsAst.New instantiation = | 278 List<jsAst.Expression> fields = |
| 419 new jsAst.New(constructor, _array(constant.fields)); | 279 constant.fields.map(constantReference).toList(growable: false); |
| 280 jsAst.New instantiation = new jsAst.New(constructor, fields); | |
| 420 return maybeAddTypeArguments(constant.type, instantiation); | 281 return maybeAddTypeArguments(constant.type, instantiation); |
| 421 } | 282 } |
| 422 | 283 |
| 423 String stripComments(String rawJavaScript) { | 284 String stripComments(String rawJavaScript) { |
| 424 return rawJavaScript.replaceAll(COMMENT_RE, ''); | 285 return rawJavaScript.replaceAll(COMMENT_RE, ''); |
| 425 } | 286 } |
| 426 | 287 |
| 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, | 288 jsAst.Expression maybeAddTypeArguments(InterfaceType type, |
| 432 jsAst.Expression value) { | 289 jsAst.Expression value) { |
| 433 if (type is InterfaceType && | 290 if (type is InterfaceType && |
| 434 !type.treatAsRaw && | 291 !type.treatAsRaw && |
| 435 backend.classNeedsRti(type.element)) { | 292 backend.classNeedsRti(type.element)) { |
| 436 InterfaceType interface = type; | 293 InterfaceType interface = type; |
| 437 RuntimeTypes rti = backend.rti; | 294 RuntimeTypes rti = backend.rti; |
| 438 Iterable<String> arguments = interface.typeArguments | 295 Iterable<String> arguments = interface.typeArguments |
| 439 .map((DartType type) => | 296 .map((DartType type) => |
| 440 rti.getTypeRepresentationWithHashes(type, (_){})); | 297 rti.getTypeRepresentationWithHashes(type, (_){})); |
| 441 jsAst.Expression argumentList = | 298 jsAst.Expression argumentList = |
| 442 new jsAst.LiteralString('[${arguments.join(', ')}]'); | 299 new jsAst.LiteralString('[${arguments.join(', ')}]'); |
| 443 return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), | 300 return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), |
| 444 [value, argumentList]); | 301 [value, argumentList]); |
| 445 } | 302 } |
| 446 return value; | 303 return value; |
| 447 } | 304 } |
| 448 | 305 |
| 449 @override | 306 @override |
| 450 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { | 307 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { |
| 451 return constantEmitter.reference(constant.referenced); | 308 return constantReference(constant.referenced); |
| 452 } | 309 } |
| 453 } | 310 } |
| OLD | NEW |