| 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 typedef jsAst.Expression _ConstantReferenceGenerator(ConstantValue constant); | 7 typedef jsAst.Expression _ConstantReferenceGenerator(ConstantValue constant); |
| 8 | 8 |
| 9 typedef jsAst.Expression _ConstantListGenerator(jsAst.Expression array); | 9 typedef jsAst.Expression _ConstantListGenerator(jsAst.Expression array); |
| 10 | 10 |
| 11 /** | 11 /** |
| 12 * Generates the JavaScript expressions for constants. | 12 * Generates the JavaScript expressions for constants. |
| 13 * | 13 * |
| 14 * It uses a given [constantReferenceGenerator] to reference nested constants | 14 * It uses a given [constantReferenceGenerator] to reference nested constants |
| 15 * (if there are some). It is hence up to that function to decide which | 15 * (if there are some). It is hence up to that function to decide which |
| 16 * constants should be inlined or not. | 16 * constants should be inlined or not. |
| 17 */ | 17 */ |
| 18 class ConstantEmitter | 18 class ConstantEmitter implements ConstantValueVisitor<jsAst.Expression, Null> { |
| 19 implements ConstantValueVisitor<jsAst.Expression, Null> { | |
| 20 | |
| 21 // 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 |
| 22 // of a string. | 20 // of a string. |
| 23 static final RegExp COMMENT_RE = | 21 static final RegExp COMMENT_RE = |
| 24 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true); | 22 new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''', multiLine: true); |
| 25 | 23 |
| 26 final Compiler compiler; | 24 final Compiler compiler; |
| 27 final Namer namer; | 25 final Namer namer; |
| 28 final _ConstantReferenceGenerator constantReferenceGenerator; | 26 final _ConstantReferenceGenerator constantReferenceGenerator; |
| 29 final _ConstantListGenerator makeConstantList; | 27 final _ConstantListGenerator makeConstantList; |
| 30 | 28 |
| 31 /** | 29 /** |
| 32 * The given [constantReferenceGenerator] function must, when invoked with a | 30 * The given [constantReferenceGenerator] function must, when invoked with a |
| 33 * constant, either return a reference or return its literal expression if it | 31 * constant, either return a reference or return its literal expression if it |
| 34 * can be inlined. | 32 * can be inlined. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 59 reporter.internalError(NO_LOCATION_SPANNABLE, | 57 reporter.internalError(NO_LOCATION_SPANNABLE, |
| 60 "The function constant does not need specific JS code."); | 58 "The function constant does not need specific JS code."); |
| 61 return null; | 59 return null; |
| 62 } | 60 } |
| 63 | 61 |
| 64 @override | 62 @override |
| 65 jsAst.Expression visitNull(NullConstantValue constant, [_]) { | 63 jsAst.Expression visitNull(NullConstantValue constant, [_]) { |
| 66 return new jsAst.LiteralNull(); | 64 return new jsAst.LiteralNull(); |
| 67 } | 65 } |
| 68 | 66 |
| 69 static final _exponentialRE = new RegExp( | 67 static final _exponentialRE = new RegExp('^' |
| 70 '^' | 68 '\([-+]?\)' // 1: sign |
| 71 '\([-+]?\)' // 1: sign | 69 '\([0-9]+\)' // 2: leading digit(s) |
| 72 '\([0-9]+\)' // 2: leading digit(s) | |
| 73 '\(\.\([0-9]*\)\)?' // 4: fraction digits | 70 '\(\.\([0-9]*\)\)?' // 4: fraction digits |
| 74 'e\([-+]?[0-9]+\)' // 5: exponent with sign | 71 'e\([-+]?[0-9]+\)' // 5: exponent with sign |
| 75 r'$'); | 72 r'$'); |
| 76 | 73 |
| 77 /// Reduces the size of exponential representations when minification is | 74 /// Reduces the size of exponential representations when minification is |
| 78 /// enabled. | 75 /// enabled. |
| 79 /// | 76 /// |
| 80 /// Removes the "+" after the exponential sign, and removes the "." before the | 77 /// Removes the "+" after the exponential sign, and removes the "." before the |
| 81 /// "e". For example `1.23e+5` is changed to `123e3`. | 78 /// "e". For example `1.23e+5` is changed to `123e3`. |
| 82 String _shortenExponentialRepresentation(String numberString) { | 79 String _shortenExponentialRepresentation(String numberString) { |
| 83 Match match = _exponentialRE.firstMatch(numberString); | 80 Match match = _exponentialRE.firstMatch(numberString); |
| 84 if (match == null) return numberString; | 81 if (match == null) return numberString; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 } | 144 } |
| 148 | 145 |
| 149 /** | 146 /** |
| 150 * Write the contents of the quoted string to a [CodeBuffer] in | 147 * Write the contents of the quoted string to a [CodeBuffer] in |
| 151 * a form that is valid as JavaScript string literal content. | 148 * a form that is valid as JavaScript string literal content. |
| 152 * The string is assumed quoted by double quote characters. | 149 * The string is assumed quoted by double quote characters. |
| 153 */ | 150 */ |
| 154 @override | 151 @override |
| 155 jsAst.Expression visitString(StringConstantValue constant, [_]) { | 152 jsAst.Expression visitString(StringConstantValue constant, [_]) { |
| 156 return js.escapedString(constant.primitiveValue.slowToString(), | 153 return js.escapedString(constant.primitiveValue.slowToString(), |
| 157 ascii: true); | 154 ascii: true); |
| 158 } | 155 } |
| 159 | 156 |
| 160 @override | 157 @override |
| 161 jsAst.Expression visitList(ListConstantValue constant, [_]) { | 158 jsAst.Expression visitList(ListConstantValue constant, [_]) { |
| 162 List<jsAst.Expression> elements = constant.entries | 159 List<jsAst.Expression> elements = constant.entries |
| 163 .map(constantReferenceGenerator) | 160 .map(constantReferenceGenerator) |
| 164 .toList(growable: false); | 161 .toList(growable: false); |
| 165 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); | 162 jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(elements); |
| 166 jsAst.Expression value = makeConstantList(array); | 163 jsAst.Expression value = makeConstantList(array); |
| 167 return maybeAddTypeArguments(constant.type, value); | 164 return maybeAddTypeArguments(constant.type, value); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 182 jsAst.Expression valueExpression = | 179 jsAst.Expression valueExpression = |
| 183 constantReferenceGenerator(constant.values[i]); | 180 constantReferenceGenerator(constant.values[i]); |
| 184 properties.add(new jsAst.Property(keyExpression, valueExpression)); | 181 properties.add(new jsAst.Property(keyExpression, valueExpression)); |
| 185 } | 182 } |
| 186 return new jsAst.ObjectInitializer(properties); | 183 return new jsAst.ObjectInitializer(properties); |
| 187 } | 184 } |
| 188 | 185 |
| 189 jsAst.Expression jsGeneralMap() { | 186 jsAst.Expression jsGeneralMap() { |
| 190 List<jsAst.Expression> data = <jsAst.Expression>[]; | 187 List<jsAst.Expression> data = <jsAst.Expression>[]; |
| 191 for (int i = 0; i < constant.keys.length; i++) { | 188 for (int i = 0; i < constant.keys.length; i++) { |
| 192 jsAst.Expression keyExpression = constantReferenceGenerator(constant.key
s[i]); | 189 jsAst.Expression keyExpression = |
| 190 constantReferenceGenerator(constant.keys[i]); |
| 193 jsAst.Expression valueExpression = | 191 jsAst.Expression valueExpression = |
| 194 constantReferenceGenerator(constant.values[i]); | 192 constantReferenceGenerator(constant.values[i]); |
| 195 data.add(keyExpression); | 193 data.add(keyExpression); |
| 196 data.add(valueExpression); | 194 data.add(valueExpression); |
| 197 } | 195 } |
| 198 return new jsAst.ArrayInitializer(data); | 196 return new jsAst.ArrayInitializer(data); |
| 199 } | 197 } |
| 200 | 198 |
| 201 ClassElement classElement = constant.type.element; | 199 ClassElement classElement = constant.type.element; |
| 202 String className = classElement.name; | 200 String className = classElement.name; |
| 203 | 201 |
| 204 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 202 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
| 205 | 203 |
| 206 // The arguments of the JavaScript constructor for any given Dart class | 204 // The arguments of the JavaScript constructor for any given Dart class |
| 207 // are in the same order as the members of the class element. | 205 // are in the same order as the members of the class element. |
| 208 int emittedArgumentCount = 0; | 206 int emittedArgumentCount = 0; |
| 209 classElement.implementation.forEachInstanceField( | 207 classElement.implementation.forEachInstanceField( |
| 210 (ClassElement enclosing, Element field) { | 208 (ClassElement enclosing, Element field) { |
| 211 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { | 209 if (field.name == JavaScriptMapConstant.LENGTH_NAME) { |
| 212 arguments.add( | 210 arguments |
| 213 new jsAst.LiteralNumber('${constant.keyList.entries.length}')); | 211 .add(new jsAst.LiteralNumber('${constant.keyList.entries.length}')); |
| 214 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { | 212 } else if (field.name == JavaScriptMapConstant.JS_OBJECT_NAME) { |
| 215 arguments.add(jsMap()); | 213 arguments.add(jsMap()); |
| 216 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { | 214 } else if (field.name == JavaScriptMapConstant.KEYS_NAME) { |
| 217 arguments.add(constantReferenceGenerator(constant.keyList)); | 215 arguments.add(constantReferenceGenerator(constant.keyList)); |
| 218 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { | 216 } else if (field.name == JavaScriptMapConstant.PROTO_VALUE) { |
| 219 assert(constant.protoValue != null); | 217 assert(constant.protoValue != null); |
| 220 arguments.add(constantReferenceGenerator(constant.protoValue)); | 218 arguments.add(constantReferenceGenerator(constant.protoValue)); |
| 221 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { | 219 } else if (field.name == JavaScriptMapConstant.JS_DATA_NAME) { |
| 222 arguments.add(jsGeneralMap()); | 220 arguments.add(jsGeneralMap()); |
| 223 } else { | 221 } else { |
| 224 reporter.internalError(field, | 222 reporter.internalError( |
| 225 "Compiler has unexpected field ${field.name} for " | 223 field, |
| 226 "${className}."); | 224 "Compiler has unexpected field ${field.name} for " |
| 227 } | 225 "${className}."); |
| 228 emittedArgumentCount++; | 226 } |
| 229 }, | 227 emittedArgumentCount++; |
| 230 includeSuperAndInjectedMembers: true); | 228 }, includeSuperAndInjectedMembers: true); |
| 231 if ((className == JavaScriptMapConstant.DART_STRING_CLASS && | 229 if ((className == JavaScriptMapConstant.DART_STRING_CLASS && |
| 232 emittedArgumentCount != 3) || | 230 emittedArgumentCount != 3) || |
| 233 (className == JavaScriptMapConstant.DART_PROTO_CLASS && | 231 (className == JavaScriptMapConstant.DART_PROTO_CLASS && |
| 234 emittedArgumentCount != 4) || | 232 emittedArgumentCount != 4) || |
| 235 (className == JavaScriptMapConstant.DART_GENERAL_CLASS && | 233 (className == JavaScriptMapConstant.DART_GENERAL_CLASS && |
| 236 emittedArgumentCount != 1)) { | 234 emittedArgumentCount != 1)) { |
| 237 reporter.internalError(classElement, | 235 reporter.internalError(classElement, |
| 238 "Compiler and ${className} disagree on number of fields."); | 236 "Compiler and ${className} disagree on number of fields."); |
| 239 } | 237 } |
| 240 | 238 |
| 241 jsAst.Expression constructor = | 239 jsAst.Expression constructor = |
| 242 backend.emitter.constructorAccess(classElement); | 240 backend.emitter.constructorAccess(classElement); |
| 243 jsAst.Expression value = new jsAst.New(constructor, arguments); | 241 jsAst.Expression value = new jsAst.New(constructor, arguments); |
| 244 return maybeAddTypeArguments(constant.type, value); | 242 return maybeAddTypeArguments(constant.type, value); |
| 245 } | 243 } |
| 246 | 244 |
| 247 JavaScriptBackend get backend => compiler.backend; | 245 JavaScriptBackend get backend => compiler.backend; |
| 248 | 246 |
| 249 jsAst.PropertyAccess getHelperProperty(Element helper) { | 247 jsAst.PropertyAccess getHelperProperty(Element helper) { |
| 250 return backend.emitter.staticFunctionAccess(helper); | 248 return backend.emitter.staticFunctionAccess(helper); |
| 251 } | 249 } |
| 252 | 250 |
| 253 @override | 251 @override |
| 254 jsAst.Expression visitType(TypeConstantValue constant, [_]) { | 252 jsAst.Expression visitType(TypeConstantValue constant, [_]) { |
| 255 DartType type = constant.representedType; | 253 DartType type = constant.representedType; |
| 256 jsAst.Name typeName = namer.runtimeTypeName(type.element); | 254 jsAst.Name typeName = namer.runtimeTypeName(type.element); |
| 257 return new jsAst.Call(getHelperProperty(backend.helpers.createRuntimeType), | 255 return new jsAst.Call(getHelperProperty(backend.helpers.createRuntimeType), |
| 258 [js.quoteName(typeName)]); | 256 [js.quoteName(typeName)]); |
| 259 } | 257 } |
| 260 | 258 |
| 261 @override | 259 @override |
| 262 jsAst.Expression visitInterceptor(InterceptorConstantValue constant, [_]) { | 260 jsAst.Expression visitInterceptor(InterceptorConstantValue constant, [_]) { |
| 263 ClassElement interceptorClass = constant.dispatchedType.element; | 261 ClassElement interceptorClass = constant.dispatchedType.element; |
| 264 return backend.emitter.interceptorPrototypeAccess(interceptorClass); | 262 return backend.emitter.interceptorPrototypeAccess(interceptorClass); |
| 265 } | 263 } |
| 266 | 264 |
| 267 @override | 265 @override |
| 268 jsAst.Expression visitSynthetic(SyntheticConstantValue constant, [_]) { | 266 jsAst.Expression visitSynthetic(SyntheticConstantValue constant, [_]) { |
| 269 switch (constant.kind) { | 267 switch (constant.kind) { |
| 270 case SyntheticConstantKind.DUMMY_INTERCEPTOR: | 268 case SyntheticConstantKind.DUMMY_INTERCEPTOR: |
| 271 case SyntheticConstantKind.EMPTY_VALUE: | 269 case SyntheticConstantKind.EMPTY_VALUE: |
| 272 return new jsAst.LiteralNumber('0'); | 270 return new jsAst.LiteralNumber('0'); |
| 273 case SyntheticConstantKind.TYPEVARIABLE_REFERENCE: | 271 case SyntheticConstantKind.TYPEVARIABLE_REFERENCE: |
| 274 case SyntheticConstantKind.NAME: | 272 case SyntheticConstantKind.NAME: |
| 275 return constant.payload; | 273 return constant.payload; |
| 276 default: | 274 default: |
| 277 reporter.internalError(NO_LOCATION_SPANNABLE, | 275 reporter.internalError(NO_LOCATION_SPANNABLE, |
| 278 "Unexpected DummyConstantKind ${constant.kind}"); | 276 "Unexpected DummyConstantKind ${constant.kind}"); |
| 279 return null; | 277 return null; |
| 280 } | 278 } |
| 281 } | 279 } |
| 282 | 280 |
| 283 @override | 281 @override |
| 284 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { | 282 jsAst.Expression visitConstructed(ConstructedConstantValue constant, [_]) { |
| 285 Element element = constant.type.element; | 283 Element element = constant.type.element; |
| 286 if (backend.isForeign(element) | 284 if (backend.isForeign(element) && element.name == 'JS_CONST') { |
| 287 && element.name == 'JS_CONST') { | |
| 288 StringConstantValue str = constant.fields.values.single; | 285 StringConstantValue str = constant.fields.values.single; |
| 289 String value = str.primitiveValue.slowToString(); | 286 String value = str.primitiveValue.slowToString(); |
| 290 return new jsAst.LiteralExpression(stripComments(value)); | 287 return new jsAst.LiteralExpression(stripComments(value)); |
| 291 } | 288 } |
| 292 jsAst.Expression constructor = | 289 jsAst.Expression constructor = |
| 293 backend.emitter.constructorAccess(constant.type.element); | 290 backend.emitter.constructorAccess(constant.type.element); |
| 294 List<jsAst.Expression> fields = | 291 List<jsAst.Expression> fields = constant.fields.values |
| 295 constant.fields.values.map(constantReferenceGenerator) | 292 .map(constantReferenceGenerator) |
| 296 .toList(growable: false); | 293 .toList(growable: false); |
| 297 jsAst.New instantiation = new jsAst.New(constructor, fields); | 294 jsAst.New instantiation = new jsAst.New(constructor, fields); |
| 298 return maybeAddTypeArguments(constant.type, instantiation); | 295 return maybeAddTypeArguments(constant.type, instantiation); |
| 299 } | 296 } |
| 300 | 297 |
| 301 String stripComments(String rawJavaScript) { | 298 String stripComments(String rawJavaScript) { |
| 302 return rawJavaScript.replaceAll(COMMENT_RE, ''); | 299 return rawJavaScript.replaceAll(COMMENT_RE, ''); |
| 303 } | 300 } |
| 304 | 301 |
| 305 jsAst.Expression maybeAddTypeArguments(InterfaceType type, | 302 jsAst.Expression maybeAddTypeArguments( |
| 306 jsAst.Expression value) { | 303 InterfaceType type, jsAst.Expression value) { |
| 307 if (type is InterfaceType && | 304 if (type is InterfaceType && |
| 308 !type.treatAsRaw && | 305 !type.treatAsRaw && |
| 309 backend.classNeedsRti(type.element)) { | 306 backend.classNeedsRti(type.element)) { |
| 310 InterfaceType interface = type; | 307 InterfaceType interface = type; |
| 311 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; | 308 RuntimeTypesEncoder rtiEncoder = backend.rtiEncoder; |
| 312 Iterable<jsAst.Expression> arguments = interface.typeArguments | 309 Iterable<jsAst.Expression> arguments = interface.typeArguments.map( |
| 313 .map((DartType type) => | 310 (DartType type) => |
| 314 rtiEncoder.getTypeRepresentationWithPlaceholders(type, (_){})); | 311 rtiEncoder.getTypeRepresentationWithPlaceholders(type, (_) {})); |
| 315 jsAst.Expression argumentList = | 312 jsAst.Expression argumentList = |
| 316 new jsAst.ArrayInitializer(arguments.toList()); | 313 new jsAst.ArrayInitializer(arguments.toList()); |
| 317 return new jsAst.Call( | 314 return new jsAst.Call( |
| 318 getHelperProperty(backend.helpers.setRuntimeTypeInfo), | 315 getHelperProperty(backend.helpers.setRuntimeTypeInfo), |
| 319 [value, argumentList]); | 316 [value, argumentList]); |
| 320 } | 317 } |
| 321 return value; | 318 return value; |
| 322 } | 319 } |
| 323 | 320 |
| 324 @override | 321 @override |
| 325 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { | 322 jsAst.Expression visitDeferred(DeferredConstantValue constant, [_]) { |
| 326 return constantReferenceGenerator(constant.referenced); | 323 return constantReferenceGenerator(constant.referenced); |
| 327 } | 324 } |
| 328 } | 325 } |
| OLD | NEW |