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 class ConstantEmitter implements ConstantVisitor { | |
6 final Compiler compiler; | |
7 final Namer namer; | |
8 | |
9 CodeBuffer buffer; | |
10 bool shouldEmitCanonicalVersion; | |
11 | |
12 ConstantEmitter(this.compiler, this.namer); | |
13 | |
14 /** | |
15 * Unless the constant can be emitted multiple times (as for numbers and | |
16 * strings) use the canonical name. | |
17 */ | |
18 void emitCanonicalVersionOfConstant(Constant constant, CodeBuffer newBuffer) { | |
19 shouldEmitCanonicalVersion = true; | |
20 buffer = newBuffer; | |
21 _visit(constant); | |
22 } | |
23 | |
24 /** | |
25 * Emit the JavaScript code of the constant. If the constant must be | |
26 * canonicalized this method emits the initialization value. | |
27 */ | |
28 void emitJavaScriptCodeForConstant(Constant constant, CodeBuffer newBuffer) { | |
29 shouldEmitCanonicalVersion = false; | |
30 buffer = newBuffer; | |
31 _visit(constant); | |
32 } | |
33 | |
34 _visit(Constant constant) { | |
35 constant.accept(this); | |
36 } | |
37 | |
38 void visitSentinel(SentinelConstant constant) { | |
39 if (shouldEmitCanonicalVersion) { | |
40 buffer.add(namer.CURRENT_ISOLATE); | |
41 } else { | |
42 compiler.internalError( | |
43 "The parameter sentinel constant does not need specific JS code"); | |
44 } | |
45 } | |
46 | |
47 void visitFunction(FunctionConstant constant) { | |
48 if (shouldEmitCanonicalVersion) { | |
49 buffer.add(namer.isolatePropertiesAccess(constant.element)); | |
50 } else { | |
51 compiler.internalError( | |
52 "The function constant does not need specific JS code"); | |
53 } | |
54 } | |
55 | |
56 void visitNull(NullConstant constant) { | |
57 buffer.add("null"); | |
58 } | |
59 | |
60 void visitInt(IntConstant constant) { | |
61 buffer.add(constant.value.toString()); | |
62 } | |
63 | |
64 void visitDouble(DoubleConstant constant) { | |
65 double value = constant.value; | |
66 if (value.isNaN()) { | |
67 buffer.add("(0/0)"); | |
68 } else if (value == double.INFINITY) { | |
69 buffer.add("(1/0)"); | |
70 } else if (value == -double.INFINITY) { | |
71 buffer.add("(-1/0)"); | |
72 } else { | |
73 buffer.add("$value"); | |
74 } | |
75 } | |
76 | |
77 void visitTrue(TrueConstant constant) { | |
78 buffer.add("true"); | |
79 } | |
80 | |
81 void visitFalse(FalseConstant constant) { | |
82 buffer.add("false"); | |
83 } | |
84 | |
85 /** | |
86 * Write the contents of the quoted string to a [CodeBuffer] in | |
87 * a form that is valid as JavaScript string literal content. | |
88 * The string is assumed quoted by single quote characters. | |
89 */ | |
90 void writeEscapedString(DartString string, | |
91 CodeBuffer buffer, | |
92 Node diagnosticNode) { | |
93 Iterator<int> iterator = string.iterator(); | |
94 while (iterator.hasNext()) { | |
95 int code = iterator.next(); | |
96 if (code === $SQ) { | |
97 buffer.add(@"\'"); | |
98 } else if (code === $LF) { | |
99 buffer.add(@'\n'); | |
100 } else if (code === $CR) { | |
101 buffer.add(@'\r'); | |
102 } else if (code === $LS) { | |
103 // This Unicode line terminator and $PS are invalid in JS string | |
104 // literals. | |
105 buffer.add(@'\u2028'); | |
106 } else if (code === $PS) { | |
107 buffer.add(@'\u2029'); | |
108 } else if (code === $BACKSLASH) { | |
109 buffer.add(@'\\'); | |
110 } else { | |
111 if (code > 0xffff) { | |
112 compiler.reportError( | |
113 diagnosticNode, | |
114 'Unhandled non-BMP character: U+${code.toRadixString(16)}'); | |
115 } | |
116 // TODO(lrn): Consider whether all codes above 0x7f really need to | |
117 // be escaped. We build a Dart string here, so it should be a literal | |
118 // stage that converts it to, e.g., UTF-8 for a JS interpreter. | |
119 if (code < 0x20) { | |
120 buffer.add(@'\x'); | |
121 if (code < 0x10) buffer.add('0'); | |
122 buffer.add(code.toRadixString(16)); | |
123 } else if (code >= 0x80) { | |
124 if (code < 0x100) { | |
125 buffer.add(@'\x'); | |
126 } else { | |
127 buffer.add(@'\u'); | |
128 if (code < 0x1000) { | |
129 buffer.add('0'); | |
130 } | |
131 } | |
132 buffer.add(code.toRadixString(16)); | |
133 } else { | |
134 buffer.addCharCode(code); | |
135 } | |
136 } | |
137 } | |
138 } | |
139 | |
140 void visitString(StringConstant constant) { | |
141 buffer.add("'"); | |
142 writeEscapedString(constant.value, buffer, constant.node); | |
143 buffer.add("'"); | |
144 } | |
145 | |
146 void emitCanonicalVersion(Constant constant) { | |
147 String name = namer.constantName(constant); | |
148 buffer.add(namer.isolatePropertiesAccessForConstant(name)); | |
149 } | |
150 | |
151 void visitList(ListConstant constant) { | |
152 if (shouldEmitCanonicalVersion) { | |
153 emitCanonicalVersion(constant); | |
154 } else { | |
155 shouldEmitCanonicalVersion = true; | |
156 buffer.add("${namer.ISOLATE}.makeConstantList"); | |
157 buffer.add("(["); | |
158 for (int i = 0; i < constant.entries.length; i++) { | |
159 if (i != 0) buffer.add(", "); | |
160 _visit(constant.entries[i]); | |
161 } | |
162 buffer.add("])"); | |
163 } | |
164 } | |
165 | |
166 String getJsConstructor(ClassElement element) { | |
167 return namer.isolatePropertiesAccess(element); | |
168 } | |
169 | |
170 void visitMap(MapConstant constant) { | |
171 if (shouldEmitCanonicalVersion) { | |
172 emitCanonicalVersion(constant); | |
173 } else { | |
174 void writeJsMap() { | |
175 buffer.add("{"); | |
176 int valueIndex = 0; | |
177 for (int i = 0; i < constant.keys.entries.length; i++) { | |
178 StringConstant key = constant.keys.entries[i]; | |
179 if (key.value == MapConstant.PROTO_PROPERTY) continue; | |
180 | |
181 if (valueIndex != 0) buffer.add(", "); | |
182 | |
183 // Keys in literal maps must be emitted in place. | |
184 emitJavaScriptCodeForConstant(key, buffer); | |
185 | |
186 buffer.add(": "); | |
187 emitCanonicalVersionOfConstant(constant.values[valueIndex++], buffer); | |
188 } | |
189 buffer.add("}"); | |
190 if (valueIndex != constant.values.length) { | |
191 compiler.internalError("Bad value count."); | |
192 } | |
193 } | |
194 | |
195 void badFieldCountError() { | |
196 compiler.internalError( | |
197 "Compiler and ConstantMap disagree on number of fields."); | |
198 } | |
199 | |
200 shouldEmitCanonicalVersion = true; | |
201 | |
202 ClassElement classElement = constant.type.element; | |
203 buffer.add("new "); | |
204 buffer.add(getJsConstructor(classElement)); | |
205 buffer.add("("); | |
206 // The arguments of the JavaScript constructor for any given Dart class | |
207 // are in the same order as the members of the class element. | |
208 int emittedArgumentCount = 0; | |
209 classElement.forEachInstanceField( | |
210 includeBackendMembers: true, | |
211 includeSuperMembers: true, | |
212 f: (ClassElement enclosing, Element field) { | |
213 if (emittedArgumentCount != 0) buffer.add(", "); | |
214 if (field.name == MapConstant.LENGTH_NAME) { | |
215 buffer.add(constant.keys.entries.length); | |
216 } else if (field.name == MapConstant.JS_OBJECT_NAME) { | |
217 writeJsMap(); | |
218 } else if (field.name == MapConstant.KEYS_NAME) { | |
219 emitCanonicalVersionOfConstant(constant.keys, buffer); | |
220 } else if (field.name == MapConstant.PROTO_VALUE) { | |
221 assert(constant.protoValue !== null); | |
222 emitCanonicalVersionOfConstant(constant.protoValue, buffer); | |
223 } else { | |
224 badFieldCountError(); | |
225 } | |
226 emittedArgumentCount++; | |
227 }); | |
228 if ((constant.protoValue === null && emittedArgumentCount != 3) || | |
229 (constant.protoValue !== null && emittedArgumentCount != 4)) { | |
230 badFieldCountError(); | |
231 } | |
232 buffer.add(")"); | |
233 } | |
234 } | |
235 | |
236 void visitConstructed(ConstructedConstant constant) { | |
237 if (shouldEmitCanonicalVersion) { | |
238 emitCanonicalVersion(constant); | |
239 } else { | |
240 shouldEmitCanonicalVersion = true; | |
241 | |
242 buffer.add("new "); | |
243 buffer.add(getJsConstructor(constant.type.element)); | |
244 buffer.add("("); | |
245 for (int i = 0; i < constant.fields.length; i++) { | |
246 if (i != 0) buffer.add(", "); | |
247 _visit(constant.fields[i]); | |
248 } | |
249 buffer.add(")"); | |
250 } | |
251 } | |
252 } | |
OLD | NEW |