| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of js_backend; | |
| 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 /** | |
| 54 * Visitor for generating JavaScript expressions to refer to [ConstantValue]s. | |
| 55 * Do not use directly, use methods from [ConstantEmitter]. | |
| 56 */ | |
| 57 class ConstantReferenceEmitter | |
| 58 implements ConstantValueVisitor<jsAst.Expression> { | |
| 59 final Compiler compiler; | |
| 60 final Namer namer; | |
| 61 | |
| 62 final ConstantEmitter constantEmitter; | |
| 63 | |
| 64 ConstantReferenceEmitter(this.compiler, this.namer, this.constantEmitter); | |
| 65 | |
| 66 jsAst.Expression generate(ConstantValue constant) { | |
| 67 return _visit(constant); | |
| 68 } | |
| 69 | |
| 70 jsAst.Expression _visit(ConstantValue constant) { | |
| 71 return constant.accept(this); | |
| 72 } | |
| 73 | |
| 74 jsAst.Expression emitCanonicalVersion(ConstantValue constant) { | |
| 75 String name = namer.constantName(constant); | |
| 76 return new jsAst.PropertyAccess.field( | |
| 77 new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name); | |
| 78 } | |
| 79 | |
| 80 jsAst.Expression literal(ConstantValue constant) { | |
| 81 return constantEmitter.literal(constant); | |
| 82 } | |
| 83 | |
| 84 jsAst.Expression visitFunction(FunctionConstantValue constant) { | |
| 85 return namer.isolateStaticClosureAccess(constant.element); | |
| 86 } | |
| 87 | |
| 88 jsAst.Expression visitNull(NullConstantValue constant) { | |
| 89 return literal(constant); | |
| 90 } | |
| 91 | |
| 92 jsAst.Expression visitInt(IntConstantValue constant) { | |
| 93 return literal(constant); | |
| 94 } | |
| 95 | |
| 96 jsAst.Expression visitDouble(DoubleConstantValue constant) { | |
| 97 return literal(constant); | |
| 98 } | |
| 99 | |
| 100 jsAst.Expression visitTrue(TrueConstantValue constant) { | |
| 101 return literal(constant); | |
| 102 } | |
| 103 | |
| 104 jsAst.Expression visitFalse(FalseConstantValue constant) { | |
| 105 return literal(constant); | |
| 106 } | |
| 107 | |
| 108 /** | |
| 109 * Write the contents of the quoted string to a [CodeBuffer] in | |
| 110 * a form that is valid as JavaScript string literal content. | |
| 111 * The string is assumed quoted by double quote characters. | |
| 112 */ | |
| 113 jsAst.Expression visitString(StringConstantValue constant) { | |
| 114 // TODO(sra): If the string is long *and repeated* (and not on a hot path) | |
| 115 // then it should be assigned to a name. We don't have reference counts (or | |
| 116 // profile information) here, so this is the wrong place. | |
| 117 return literal(constant); | |
| 118 } | |
| 119 | |
| 120 jsAst.Expression visitList(ListConstantValue constant) { | |
| 121 return emitCanonicalVersion(constant); | |
| 122 } | |
| 123 | |
| 124 jsAst.Expression visitMap(MapConstantValue constant) { | |
| 125 return emitCanonicalVersion(constant); | |
| 126 } | |
| 127 | |
| 128 jsAst.Expression visitType(TypeConstantValue constant) { | |
| 129 return emitCanonicalVersion(constant); | |
| 130 } | |
| 131 | |
| 132 jsAst.Expression visitConstructed(ConstructedConstantValue constant) { | |
| 133 return emitCanonicalVersion(constant); | |
| 134 } | |
| 135 | |
| 136 jsAst.Expression visitInterceptor(InterceptorConstantValue constant) { | |
| 137 return emitCanonicalVersion(constant); | |
| 138 } | |
| 139 | |
| 140 jsAst.Expression visitDummy(DummyConstantValue constant) { | |
| 141 return literal(constant); | |
| 142 } | |
| 143 | |
| 144 jsAst.Expression visitDeferred(DeferredConstantValue constant) { | |
| 145 return emitCanonicalVersion(constant); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 /** | |
| 150 * Visitor for generating JavaScript expressions that litterally represent | |
| 151 * [ConstantValue]s. These can be used for inlining constants or in | |
| 152 * initializers. Do not use directly, use methods from [ConstantEmitter]. | |
| 153 */ | |
| 154 class ConstantLiteralEmitter implements ConstantValueVisitor<jsAst.Expression> { | |
| 155 | |
| 156 // Matches blank lines, comment lines and trailing comments that can't be part | |
| 157 // of a string. | |
| 158 static final RegExp COMMENT_RE = | |
| 159 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); | |
| 160 | |
| 161 final Compiler compiler; | |
| 162 final Namer namer; | |
| 163 final jsAst.Template makeConstantListTemplate; | |
| 164 final ConstantEmitter constantEmitter; | |
| 165 | |
| 166 ConstantLiteralEmitter(this.compiler, | |
| 167 this.namer, | |
| 168 this.makeConstantListTemplate, | |
| 169 this.constantEmitter); | |
| 170 | |
| 171 jsAst.Expression generate(ConstantValue constant) { | |
| 172 return _visit(constant); | |
| 173 } | |
| 174 | |
| 175 jsAst.Expression _visit(ConstantValue constant) { | |
| 176 return constant.accept(this); | |
| 177 } | |
| 178 | |
| 179 jsAst.Expression visitFunction(FunctionConstantValue constant) { | |
| 180 compiler.internalError(NO_LOCATION_SPANNABLE, | |
| 181 "The function constant does not need specific JS code."); | |
| 182 return null; | |
| 183 } | |
| 184 | |
| 185 jsAst.Expression visitNull(NullConstantValue constant) { | |
| 186 return new jsAst.LiteralNull(); | |
| 187 } | |
| 188 | |
| 189 jsAst.Expression visitInt(IntConstantValue constant) { | |
| 190 return new jsAst.LiteralNumber('${constant.primitiveValue}'); | |
| 191 } | |
| 192 | |
| 193 jsAst.Expression visitDouble(DoubleConstantValue constant) { | |
| 194 double value = constant.primitiveValue; | |
| 195 if (value.isNaN) { | |
| 196 return js("0/0"); | |
| 197 } else if (value == double.INFINITY) { | |
| 198 return js("1/0"); | |
| 199 } else if (value == -double.INFINITY) { | |
| 200 return js("-1/0"); | |
| 201 } else { | |
| 202 return new jsAst.LiteralNumber("$value"); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 jsAst.Expression visitTrue(TrueConstantValue constant) { | |
| 207 if (compiler.enableMinification) { | |
| 208 // Use !0 for true. | |
| 209 return js("!0"); | |
| 210 } else { | |
| 211 return js('true'); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 jsAst.Expression visitFalse(FalseConstantValue constant) { | |
| 216 if (compiler.enableMinification) { | |
| 217 // Use !1 for false. | |
| 218 return js("!1"); | |
| 219 } else { | |
| 220 return js('false'); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 /** | |
| 225 * Write the contents of the quoted string to a [CodeBuffer] in | |
| 226 * a form that is valid as JavaScript string literal content. | |
| 227 * The string is assumed quoted by double quote characters. | |
| 228 */ | |
| 229 jsAst.Expression visitString(StringConstantValue constant) { | |
| 230 StringBuffer sb = new StringBuffer(); | |
| 231 writeJsonEscapedCharsOn(constant.primitiveValue.slowToString(), sb); | |
| 232 return new jsAst.LiteralString('"$sb"'); | |
| 233 } | |
| 234 | |
| 235 jsAst.Expression visitList(ListConstantValue constant) { | |
| 236 List<jsAst.Expression> elements = _array(constant.entries); | |
| 237 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer.from(elements); | |
| 238 jsAst.Expression value = makeConstantListTemplate.instantiate([array]); | |
| 239 return maybeAddTypeArguments(constant.type, value); | |
| 240 } | |
| 241 | |
| 242 jsAst.Expression getJsConstructor(ClassElement element) { | |
| 243 return namer.elementAccess(element); | |
| 244 } | |
| 245 | |
| 246 jsAst.Expression visitMap(JavaScriptMapConstant constant) { | |
| 247 jsAst.Expression jsMap() { | |
| 248 List<jsAst.Property> properties = <jsAst.Property>[]; | |
| 249 for (int i = 0; i < constant.length; i++) { | |
| 250 StringConstantValue key = constant.keys[i]; | |
| 251 if (key.primitiveValue == JavaScriptMapConstant.PROTO_PROPERTY) { | |
| 252 continue; | |
| 253 } | |
| 254 | |
| 255 // Keys in literal maps must be emitted in place. | |
| 256 jsAst.Literal keyExpression = _visit(key); | |
| 257 jsAst.Expression valueExpression = | |
| 258 constantEmitter.reference(constant.values[i]); | |
| 259 properties.add(new jsAst.Property(keyExpression, valueExpression)); | |
| 260 } | |
| 261 return new jsAst.ObjectInitializer(properties); | |
| 262 } | |
| 263 | |
| 264 jsAst.Expression jsGeneralMap() { | |
| 265 List<jsAst.Expression> data = <jsAst.Expression>[]; | |
| 266 for (int i = 0; i < constant.keys.length; i++) { | |
| 267 jsAst.Expression keyExpression = | |
| 268 constantEmitter.reference(constant.keys[i]); | |
| 269 jsAst.Expression valueExpression = | |
| 270 constantEmitter.reference(constant.values[i]); | |
| 271 data.add(keyExpression); | |
| 272 data.add(valueExpression); | |
| 273 } | |
| 274 return new jsAst.ArrayInitializer.from(data); | |
| 275 } | |
| 276 | |
| 277 ClassElement classElement = constant.type.element; | |
| 278 String className = classElement.name; | |
| 279 | |
| 280 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | |
| 281 | |
| 282 // The arguments of the JavaScript constructor for any given Dart class | |
| 283 // are in the same order as the members of the class element. | |
| 284 int emittedArgumentCount = 0; | |
| 285 classElement.implementation.forEachInstanceField( | |
| 286 (ClassElement enclosing, Element field) { | |
| 287 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { | |
| 288 arguments.add( | |
| 289 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); | |
| 290 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { | |
| 291 arguments.add(jsMap()); | |
| 292 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { | |
| 293 arguments.add(constantEmitter.reference(constant.keyList)); | |
| 294 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { | |
| 295 assert(constant.protoValue != null); | |
| 296 arguments.add(constantEmitter.reference(constant.protoValue)); | |
| 297 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { | |
| 298 arguments.add(jsGeneralMap()); | |
| 299 } else { | |
| 300 compiler.internalError(field, | |
| 301 "Compiler has unexpected field ${field.name} for " | |
| 302 "${className}."); | |
| 303 } | |
| 304 emittedArgumentCount++; | |
| 305 }, | |
| 306 includeSuperAndInjectedMembers: true); | |
| 307 if ((className == JavaScriptMapConstant.DART_STRING_CLASS && | |
| 308 emittedArgumentCount != 3) || | |
| 309 (className == JavaScriptMapConstant.DART_PROTO_CLASS && | |
| 310 emittedArgumentCount != 4) || | |
| 311 (className == JavaScriptMapConstant.DART_GENERAL_CLASS && | |
| 312 emittedArgumentCount != 1)) { | |
| 313 compiler.internalError(classElement, | |
| 314 "Compiler and ${className} disagree on number of fields."); | |
| 315 } | |
| 316 | |
| 317 jsAst.Expression value = | |
| 318 new jsAst.New(getJsConstructor(classElement), arguments); | |
| 319 return maybeAddTypeArguments(constant.type, value); | |
| 320 } | |
| 321 | |
| 322 JavaScriptBackend get backend => compiler.backend; | |
| 323 | |
| 324 jsAst.PropertyAccess getHelperProperty(Element helper) { | |
| 325 return backend.namer.elementAccess(helper); | |
| 326 } | |
| 327 | |
| 328 jsAst.Expression visitType(TypeConstantValue constant) { | |
| 329 DartType type = constant.representedType; | |
| 330 String name = namer.getRuntimeTypeName(type.element); | |
| 331 jsAst.Expression typeName = new jsAst.LiteralString("'$name'"); | |
| 332 return new jsAst.Call(getHelperProperty(backend.getCreateRuntimeType()), | |
| 333 [typeName]); | |
| 334 } | |
| 335 | |
| 336 jsAst.Expression visitInterceptor(InterceptorConstantValue constant) { | |
| 337 return new jsAst.PropertyAccess.field( | |
| 338 getJsConstructor(constant.dispatchedType.element), | |
| 339 'prototype'); | |
| 340 } | |
| 341 | |
| 342 jsAst.Expression visitDummy(DummyConstantValue constant) { | |
| 343 return new jsAst.LiteralNumber('0'); | |
| 344 } | |
| 345 | |
| 346 jsAst.Expression visitConstructed(ConstructedConstantValue constant) { | |
| 347 Element element = constant.type.element; | |
| 348 if (element.isForeign(backend) | |
| 349 && element.name == 'JS_CONST') { | |
| 350 StringConstantValue str = constant.fields[0]; | |
| 351 String value = str.primitiveValue.slowToString(); | |
| 352 return new jsAst.LiteralExpression(stripComments(value)); | |
| 353 } | |
| 354 jsAst.New instantiation = new jsAst.New( | |
| 355 getJsConstructor(constant.type.element), | |
| 356 _array(constant.fields)); | |
| 357 return maybeAddTypeArguments(constant.type, instantiation); | |
| 358 } | |
| 359 | |
| 360 String stripComments(String rawJavaScript) { | |
| 361 return rawJavaScript.replaceAll(COMMENT_RE, ''); | |
| 362 } | |
| 363 | |
| 364 List<jsAst.Expression> _array(List<ConstantValue> values) { | |
| 365 List<jsAst.Expression> valueList = <jsAst.Expression>[]; | |
| 366 for (int i = 0; i < values.length; i++) { | |
| 367 valueList.add(constantEmitter.reference(values[i])); | |
| 368 } | |
| 369 return valueList; | |
| 370 } | |
| 371 | |
| 372 jsAst.Expression maybeAddTypeArguments(InterfaceType type, | |
| 373 jsAst.Expression value) { | |
| 374 if (type is InterfaceType && | |
| 375 !type.treatAsRaw && | |
| 376 backend.classNeedsRti(type.element)) { | |
| 377 InterfaceType interface = type; | |
| 378 RuntimeTypes rti = backend.rti; | |
| 379 Iterable<String> arguments = interface.typeArguments | |
| 380 .map((DartType type) => | |
| 381 rti.getTypeRepresentationWithHashes(type, (_){})); | |
| 382 jsAst.Expression argumentList = | |
| 383 new jsAst.LiteralString('[${arguments.join(', ')}]'); | |
| 384 return new jsAst.Call(getHelperProperty(backend.getSetRuntimeTypeInfo()), | |
| 385 [value, argumentList]); | |
| 386 } | |
| 387 return value; | |
| 388 } | |
| 389 | |
| 390 jsAst.Expression visitDeferred(DeferredConstantValue constant) { | |
| 391 return constantEmitter.reference(constant.referenced); | |
| 392 } | |
| 393 } | |
| OLD | NEW |