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 |