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 |