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 class ConstantEmitter { | 7 class ConstantEmitter { |
8 ConstantReferenceEmitter _referenceEmitter; | 8 ConstantReferenceEmitter _referenceEmitter; |
9 ConstantInitializerEmitter _initializerEmitter; | 9 ConstantInitializerEmitter _initializerEmitter; |
10 | 10 |
11 ConstantEmitter(Compiler compiler, Namer namer) { | 11 ConstantEmitter(Compiler compiler, Namer namer) { |
12 _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); | 12 _referenceEmitter = new ConstantReferenceEmitter(compiler, namer); |
13 _initializerEmitter = new ConstantInitializerEmitter( | 13 _initializerEmitter = new ConstantInitializerEmitter( |
14 compiler, namer, _referenceEmitter); | 14 compiler, namer, _referenceEmitter); |
15 } | 15 } |
16 | 16 |
17 /** | 17 /** |
18 * Constructs an expression that is a reference to the constant. Uses a | 18 * Constructs an expression that is a reference to the constant. Uses a |
19 * canonical name unless the constant can be emitted multiple times (as for | 19 * canonical name unless the constant can be emitted multiple times (as for |
20 * numbers and strings). | 20 * numbers and strings). |
21 */ | 21 */ |
22 js.Expression reference(Constant constant) { | 22 jsAst.Expression reference(Constant constant) { |
23 return _referenceEmitter.generate(constant); | 23 return _referenceEmitter.generate(constant); |
24 } | 24 } |
25 | 25 |
26 /** | 26 /** |
27 * Constructs an expression like [reference], but the expression is valid | 27 * Constructs an expression like [reference], but the expression is valid |
28 * during isolate initialization. | 28 * during isolate initialization. |
29 */ | 29 */ |
30 js.Expression referenceInInitializationContext(Constant constant) { | 30 jsAst.Expression referenceInInitializationContext(Constant constant) { |
31 return _referenceEmitter.generateInInitializationContext(constant); | 31 return _referenceEmitter.generateInInitializationContext(constant); |
32 } | 32 } |
33 | 33 |
34 /** | 34 /** |
35 * Constructs an expression used to initialize a canonicalized constant. | 35 * Constructs an expression used to initialize a canonicalized constant. |
36 */ | 36 */ |
37 js.Expression initializationExpression(Constant constant) { | 37 jsAst.Expression initializationExpression(Constant constant) { |
38 return _initializerEmitter.generate(constant); | 38 return _initializerEmitter.generate(constant); |
39 } | 39 } |
40 } | 40 } |
41 | 41 |
42 /** | 42 /** |
43 * Visitor for generating JavaScript expressions to refer to [Constant]s. | 43 * Visitor for generating JavaScript expressions to refer to [Constant]s. |
44 * Do not use directly, use methods from [ConstantEmitter]. | 44 * Do not use directly, use methods from [ConstantEmitter]. |
45 */ | 45 */ |
46 class ConstantReferenceEmitter implements ConstantVisitor<js.Expression> { | 46 class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> { |
47 final Compiler compiler; | 47 final Compiler compiler; |
48 final Namer namer; | 48 final Namer namer; |
49 bool inIsolateInitializationContext = false; | 49 bool inIsolateInitializationContext = false; |
50 | 50 |
51 ConstantReferenceEmitter(this.compiler, this.namer); | 51 ConstantReferenceEmitter(this.compiler, this.namer); |
52 | 52 |
53 js.Expression generate(Constant constant) { | 53 jsAst.Expression generate(Constant constant) { |
54 inIsolateInitializationContext = false; | 54 inIsolateInitializationContext = false; |
55 return _visit(constant); | 55 return _visit(constant); |
56 } | 56 } |
57 | 57 |
58 js.Expression generateInInitializationContext(Constant constant) { | 58 jsAst.Expression generateInInitializationContext(Constant constant) { |
59 inIsolateInitializationContext = true; | 59 inIsolateInitializationContext = true; |
60 return _visit(constant); | 60 return _visit(constant); |
61 } | 61 } |
62 | 62 |
63 js.Expression _visit(Constant constant) { | 63 jsAst.Expression _visit(Constant constant) { |
64 return constant.accept(this); | 64 return constant.accept(this); |
65 } | 65 } |
66 | 66 |
67 js.Expression visitSentinel(SentinelConstant constant) { | 67 jsAst.Expression visitSentinel(SentinelConstant constant) { |
68 return new js.VariableUse(namer.CURRENT_ISOLATE); | 68 return new jsAst.VariableUse(namer.CURRENT_ISOLATE); |
69 } | 69 } |
70 | 70 |
71 js.Expression visitFunction(FunctionConstant constant) { | 71 jsAst.Expression visitFunction(FunctionConstant constant) { |
72 return inIsolateInitializationContext | 72 return inIsolateInitializationContext |
73 ? new js.VariableUse(namer.isolatePropertiesAccess(constant.element)) | 73 ? new jsAst.VariableUse(namer.isolatePropertiesAccess(constant.element)) |
74 : new js.VariableUse(namer.isolateAccess(constant.element)); | 74 : new jsAst.VariableUse(namer.isolateAccess(constant.element)); |
75 } | 75 } |
76 | 76 |
77 js.Expression visitNull(NullConstant constant) { | 77 jsAst.Expression visitNull(NullConstant constant) { |
78 return new js.LiteralNull(); | 78 return new jsAst.LiteralNull(); |
79 } | 79 } |
80 | 80 |
81 js.Expression visitInt(IntConstant constant) { | 81 jsAst.Expression visitInt(IntConstant constant) { |
82 return new js.LiteralNumber('${constant.value}'); | 82 return new jsAst.LiteralNumber('${constant.value}'); |
83 } | 83 } |
84 | 84 |
85 js.Expression visitDouble(DoubleConstant constant) { | 85 jsAst.Expression visitDouble(DoubleConstant constant) { |
86 double value = constant.value; | 86 double value = constant.value; |
87 if (value.isNaN) { | 87 if (value.isNaN) { |
88 return new js.LiteralNumber("(0/0)"); | 88 return new jsAst.LiteralNumber("(0/0)"); |
89 } else if (value == double.INFINITY) { | 89 } else if (value == double.INFINITY) { |
90 return new js.LiteralNumber("(1/0)"); | 90 return new jsAst.LiteralNumber("(1/0)"); |
91 } else if (value == -double.INFINITY) { | 91 } else if (value == -double.INFINITY) { |
92 return new js.LiteralNumber("(-1/0)"); | 92 return new jsAst.LiteralNumber("(-1/0)"); |
93 } else { | 93 } else { |
94 return new js.LiteralNumber("$value"); | 94 return new jsAst.LiteralNumber("$value"); |
95 } | 95 } |
96 } | 96 } |
97 | 97 |
98 js.Expression visitTrue(TrueConstant constant) { | 98 jsAst.Expression visitTrue(TrueConstant constant) { |
99 if (compiler.enableMinification) { | 99 if (compiler.enableMinification) { |
100 // Use !0 for true. | 100 // Use !0 for true. |
101 return new js.Prefix("!", new js.LiteralNumber("0")); | 101 return new jsAst.Prefix("!", new jsAst.LiteralNumber("0")); |
102 } else { | 102 } else { |
103 return new js.LiteralBool(true); | 103 return new jsAst.LiteralBool(true); |
104 } | 104 } |
105 | 105 |
106 } | 106 } |
107 | 107 |
108 js.Expression visitFalse(FalseConstant constant) { | 108 jsAst.Expression visitFalse(FalseConstant constant) { |
109 if (compiler.enableMinification) { | 109 if (compiler.enableMinification) { |
110 // Use !1 for false. | 110 // Use !1 for false. |
111 return new js.Prefix("!", new js.LiteralNumber("1")); | 111 return new jsAst.Prefix("!", new jsAst.LiteralNumber("1")); |
112 } else { | 112 } else { |
113 return new js.LiteralBool(false); | 113 return new jsAst.LiteralBool(false); |
114 } | 114 } |
115 } | 115 } |
116 | 116 |
117 /** | 117 /** |
118 * Write the contents of the quoted string to a [CodeBuffer] in | 118 * Write the contents of the quoted string to a [CodeBuffer] in |
119 * a form that is valid as JavaScript string literal content. | 119 * a form that is valid as JavaScript string literal content. |
120 * The string is assumed quoted by double quote characters. | 120 * The string is assumed quoted by double quote characters. |
121 */ | 121 */ |
122 js.Expression visitString(StringConstant constant) { | 122 jsAst.Expression visitString(StringConstant constant) { |
123 // TODO(sra): If the string is long *and repeated* (and not on a hot path) | 123 // TODO(sra): If the string is long *and repeated* (and not on a hot path) |
124 // then it should be assigned to a name. We don't have reference counts (or | 124 // then it should be assigned to a name. We don't have reference counts (or |
125 // profile information) here, so this is the wrong place. | 125 // profile information) here, so this is the wrong place. |
126 StringBuffer sb = new StringBuffer(); | 126 StringBuffer sb = new StringBuffer(); |
127 writeJsonEscapedCharsOn(constant.value.slowToString(), sb); | 127 writeJsonEscapedCharsOn(constant.value.slowToString(), sb); |
128 return new js.LiteralString('"$sb"'); | 128 return new jsAst.LiteralString('"$sb"'); |
129 } | 129 } |
130 | 130 |
131 js.Expression emitCanonicalVersion(Constant constant) { | 131 jsAst.Expression emitCanonicalVersion(Constant constant) { |
132 String name = namer.constantName(constant); | 132 String name = namer.constantName(constant); |
133 if (inIsolateInitializationContext) { | 133 if (inIsolateInitializationContext) { |
134 // $isolateName.$isolatePropertiesName.$name | 134 // $isolateName.$isolatePropertiesName.$name |
135 return new js.PropertyAccess.field( | 135 return new jsAst.PropertyAccess.field( |
136 new js.PropertyAccess.field( | 136 new jsAst.PropertyAccess.field( |
137 new js.VariableUse(namer.isolateName), | 137 new jsAst.VariableUse(namer.isolateName), |
138 namer.isolatePropertiesName), | 138 namer.isolatePropertiesName), |
139 name); | 139 name); |
140 } else { | 140 } else { |
141 return new js.PropertyAccess.field( | 141 return new jsAst.PropertyAccess.field( |
142 new js.VariableUse(namer.CURRENT_ISOLATE), | 142 new jsAst.VariableUse(namer.CURRENT_ISOLATE), |
143 name); | 143 name); |
144 } | 144 } |
145 } | 145 } |
146 | 146 |
147 js.Expression visitList(ListConstant constant) { | 147 jsAst.Expression visitList(ListConstant constant) { |
148 return emitCanonicalVersion(constant); | 148 return emitCanonicalVersion(constant); |
149 } | 149 } |
150 | 150 |
151 js.Expression visitMap(MapConstant constant) { | 151 jsAst.Expression visitMap(MapConstant constant) { |
152 return emitCanonicalVersion(constant); | 152 return emitCanonicalVersion(constant); |
153 } | 153 } |
154 | 154 |
155 js.Expression visitType(TypeConstant constant) { | 155 jsAst.Expression visitType(TypeConstant constant) { |
156 return emitCanonicalVersion(constant); | 156 return emitCanonicalVersion(constant); |
157 } | 157 } |
158 | 158 |
159 js.Expression visitConstructed(ConstructedConstant constant) { | 159 jsAst.Expression visitConstructed(ConstructedConstant constant) { |
160 return emitCanonicalVersion(constant); | 160 return emitCanonicalVersion(constant); |
161 } | 161 } |
162 } | 162 } |
163 | 163 |
164 /** | 164 /** |
165 * Visitor for generating JavaScript expressions to initialize [Constant]s. | 165 * Visitor for generating JavaScript expressions to initialize [Constant]s. |
166 * Do not use directly; use methods from [ConstantEmitter]. | 166 * Do not use directly; use methods from [ConstantEmitter]. |
167 */ | 167 */ |
168 class ConstantInitializerEmitter implements ConstantVisitor<js.Expression> { | 168 class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> { |
169 final Compiler compiler; | 169 final Compiler compiler; |
170 final Namer namer; | 170 final Namer namer; |
171 final ConstantReferenceEmitter referenceEmitter; | 171 final ConstantReferenceEmitter referenceEmitter; |
172 | 172 |
173 ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); | 173 ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter); |
174 | 174 |
175 js.Expression generate(Constant constant) { | 175 jsAst.Expression generate(Constant constant) { |
176 return _visit(constant); | 176 return _visit(constant); |
177 } | 177 } |
178 | 178 |
179 js.Expression _visit(Constant constant) { | 179 jsAst.Expression _visit(Constant constant) { |
180 return constant.accept(this); | 180 return constant.accept(this); |
181 } | 181 } |
182 | 182 |
183 js.Expression _reference(Constant constant) { | 183 jsAst.Expression _reference(Constant constant) { |
184 return referenceEmitter.generateInInitializationContext(constant); | 184 return referenceEmitter.generateInInitializationContext(constant); |
185 } | 185 } |
186 | 186 |
187 js.Expression visitSentinel(SentinelConstant constant) { | 187 jsAst.Expression visitSentinel(SentinelConstant constant) { |
188 compiler.internalError( | 188 compiler.internalError( |
189 "The parameter sentinel constant does not need specific JS code"); | 189 "The parameter sentinel constant does not need specific JS code"); |
190 } | 190 } |
191 | 191 |
192 js.Expression visitFunction(FunctionConstant constant) { | 192 jsAst.Expression visitFunction(FunctionConstant constant) { |
193 compiler.internalError( | 193 compiler.internalError( |
194 "The function constant does not need specific JS code"); | 194 "The function constant does not need specific JS code"); |
195 } | 195 } |
196 | 196 |
197 js.Expression visitNull(NullConstant constant) { | 197 jsAst.Expression visitNull(NullConstant constant) { |
198 return _reference(constant); | 198 return _reference(constant); |
199 } | 199 } |
200 | 200 |
201 js.Expression visitInt(IntConstant constant) { | 201 jsAst.Expression visitInt(IntConstant constant) { |
202 return _reference(constant); | 202 return _reference(constant); |
203 } | 203 } |
204 | 204 |
205 js.Expression visitDouble(DoubleConstant constant) { | 205 jsAst.Expression visitDouble(DoubleConstant constant) { |
206 return _reference(constant); | 206 return _reference(constant); |
207 } | 207 } |
208 | 208 |
209 js.Expression visitTrue(TrueConstant constant) { | 209 jsAst.Expression visitTrue(TrueConstant constant) { |
210 return _reference(constant); | 210 return _reference(constant); |
211 } | 211 } |
212 | 212 |
213 js.Expression visitFalse(FalseConstant constant) { | 213 jsAst.Expression visitFalse(FalseConstant constant) { |
214 return _reference(constant); | 214 return _reference(constant); |
215 } | 215 } |
216 | 216 |
217 js.Expression visitString(StringConstant constant) { | 217 jsAst.Expression visitString(StringConstant constant) { |
218 // TODO(sra): Some larger strings are worth sharing. | 218 // TODO(sra): Some larger strings are worth sharing. |
219 return _reference(constant); | 219 return _reference(constant); |
220 } | 220 } |
221 | 221 |
222 js.Expression visitList(ListConstant constant) { | 222 jsAst.Expression visitList(ListConstant constant) { |
223 return new js.Call( | 223 return new jsAst.Call( |
224 new js.PropertyAccess.field( | 224 new jsAst.PropertyAccess.field( |
225 new js.VariableUse(namer.isolateName), | 225 new jsAst.VariableUse(namer.isolateName), |
226 'makeConstantList'), | 226 'makeConstantList'), |
227 [new js.ArrayInitializer.from(_array(constant.entries))]); | 227 [new jsAst.ArrayInitializer.from(_array(constant.entries))]); |
228 } | 228 } |
229 | 229 |
230 String getJsConstructor(ClassElement element) { | 230 String getJsConstructor(ClassElement element) { |
231 return namer.isolatePropertiesAccess(element); | 231 return namer.isolatePropertiesAccess(element); |
232 } | 232 } |
233 | 233 |
234 js.Expression visitMap(MapConstant constant) { | 234 jsAst.Expression visitMap(MapConstant constant) { |
235 js.Expression jsMap() { | 235 jsAst.Expression jsMap() { |
236 List<js.Property> properties = <js.Property>[]; | 236 List<jsAst.Property> properties = <jsAst.Property>[]; |
237 int valueIndex = 0; | 237 int valueIndex = 0; |
238 for (int i = 0; i < constant.keys.entries.length; i++) { | 238 for (int i = 0; i < constant.keys.entries.length; i++) { |
239 StringConstant key = constant.keys.entries[i]; | 239 StringConstant key = constant.keys.entries[i]; |
240 if (key.value == MapConstant.PROTO_PROPERTY) continue; | 240 if (key.value == MapConstant.PROTO_PROPERTY) continue; |
241 | 241 |
242 // Keys in literal maps must be emitted in place. | 242 // Keys in literal maps must be emitted in place. |
243 js.Literal keyExpression = _visit(key); | 243 jsAst.Literal keyExpression = _visit(key); |
244 js.Expression valueExpression = | 244 jsAst.Expression valueExpression = |
245 _reference(constant.values[valueIndex++]); | 245 _reference(constant.values[valueIndex++]); |
246 properties.add(new js.Property(keyExpression, valueExpression)); | 246 properties.add(new jsAst.Property(keyExpression, valueExpression)); |
247 } | 247 } |
248 if (valueIndex != constant.values.length) { | 248 if (valueIndex != constant.values.length) { |
249 compiler.internalError("Bad value count."); | 249 compiler.internalError("Bad value count."); |
250 } | 250 } |
251 return new js.ObjectInitializer(properties); | 251 return new jsAst.ObjectInitializer(properties); |
252 } | 252 } |
253 | 253 |
254 void badFieldCountError() { | 254 void badFieldCountError() { |
255 compiler.internalError( | 255 compiler.internalError( |
256 "Compiler and ConstantMap disagree on number of fields."); | 256 "Compiler and ConstantMap disagree on number of fields."); |
257 } | 257 } |
258 | 258 |
259 ClassElement classElement = constant.type.element; | 259 ClassElement classElement = constant.type.element; |
260 | 260 |
261 List<js.Expression> arguments = <js.Expression>[]; | 261 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
262 | 262 |
263 // The arguments of the JavaScript constructor for any given Dart class | 263 // The arguments of the JavaScript constructor for any given Dart class |
264 // are in the same order as the members of the class element. | 264 // are in the same order as the members of the class element. |
265 int emittedArgumentCount = 0; | 265 int emittedArgumentCount = 0; |
266 classElement.implementation.forEachInstanceField( | 266 classElement.implementation.forEachInstanceField( |
267 (ClassElement enclosing, Element field) { | 267 (ClassElement enclosing, Element field) { |
268 if (field.name == MapConstant.LENGTH_NAME) { | 268 if (field.name == MapConstant.LENGTH_NAME) { |
269 arguments.add( | 269 arguments.add( |
270 new js.LiteralNumber('${constant.keys.entries.length}')); | 270 new jsAst.LiteralNumber('${constant.keys.entries.length}')); |
271 } else if (field.name == MapConstant.JS_OBJECT_NAME) { | 271 } else if (field.name == MapConstant.JS_OBJECT_NAME) { |
272 arguments.add(jsMap()); | 272 arguments.add(jsMap()); |
273 } else if (field.name == MapConstant.KEYS_NAME) { | 273 } else if (field.name == MapConstant.KEYS_NAME) { |
274 arguments.add(_reference(constant.keys)); | 274 arguments.add(_reference(constant.keys)); |
275 } else if (field.name == MapConstant.PROTO_VALUE) { | 275 } else if (field.name == MapConstant.PROTO_VALUE) { |
276 assert(constant.protoValue != null); | 276 assert(constant.protoValue != null); |
277 arguments.add(_reference(constant.protoValue)); | 277 arguments.add(_reference(constant.protoValue)); |
278 } else { | 278 } else { |
279 badFieldCountError(); | 279 badFieldCountError(); |
280 } | 280 } |
281 emittedArgumentCount++; | 281 emittedArgumentCount++; |
282 }, | 282 }, |
283 includeBackendMembers: true, | 283 includeBackendMembers: true, |
284 includeSuperMembers: true); | 284 includeSuperMembers: true); |
285 | 285 |
286 if ((constant.protoValue == null && emittedArgumentCount != 3) || | 286 if ((constant.protoValue == null && emittedArgumentCount != 3) || |
287 (constant.protoValue != null && emittedArgumentCount != 4)) { | 287 (constant.protoValue != null && emittedArgumentCount != 4)) { |
288 badFieldCountError(); | 288 badFieldCountError(); |
289 } | 289 } |
290 | 290 |
291 return new js.New( | 291 return new jsAst.New( |
292 new js.VariableUse(getJsConstructor(classElement)), | 292 new jsAst.VariableUse(getJsConstructor(classElement)), |
293 arguments); | 293 arguments); |
294 } | 294 } |
295 | 295 |
296 js.Expression visitType(TypeConstant constant) { | 296 jsAst.Expression visitType(TypeConstant constant) { |
297 SourceString helperSourceName = const SourceString('createRuntimeType'); | 297 SourceString helperSourceName = const SourceString('createRuntimeType'); |
298 Element helper = compiler.findHelper(helperSourceName); | 298 Element helper = compiler.findHelper(helperSourceName); |
299 JavaScriptBackend backend = compiler.backend; | 299 JavaScriptBackend backend = compiler.backend; |
300 String helperName = backend.namer.getName(helper); | 300 String helperName = backend.namer.getName(helper); |
301 DartType type = constant.representedType; | 301 DartType type = constant.representedType; |
302 Element element = type.element; | 302 Element element = type.element; |
303 String name = backend.rti.getRawTypeRepresentation(type); | 303 String name = backend.rti.getRawTypeRepresentation(type); |
304 js.Expression typeName = new js.LiteralString("'$name'"); | 304 jsAst.Expression typeName = new jsAst.LiteralString("'$name'"); |
305 return new js.Call( | 305 return new jsAst.Call( |
306 new js.PropertyAccess.field( | 306 new jsAst.PropertyAccess.field( |
307 new js.VariableUse(namer.CURRENT_ISOLATE), | 307 new jsAst.VariableUse(namer.CURRENT_ISOLATE), |
308 helperName), | 308 helperName), |
309 [typeName]); | 309 [typeName]); |
310 } | 310 } |
311 | 311 |
312 js.Expression visitConstructed(ConstructedConstant constant) { | 312 jsAst.Expression visitConstructed(ConstructedConstant constant) { |
313 return new js.New( | 313 return new jsAst.New( |
314 new js.VariableUse(getJsConstructor(constant.type.element)), | 314 new jsAst.VariableUse(getJsConstructor(constant.type.element)), |
315 _array(constant.fields)); | 315 _array(constant.fields)); |
316 } | 316 } |
317 | 317 |
318 List<js.Expression> _array(List<Constant> values) { | 318 List<jsAst.Expression> _array(List<Constant> values) { |
319 List<js.Expression> valueList = <js.Expression>[]; | 319 List<jsAst.Expression> valueList = <jsAst.Expression>[]; |
320 for (int i = 0; i < values.length; i++) { | 320 for (int i = 0; i < values.length; i++) { |
321 valueList.add(_reference(values[i])); | 321 valueList.add(_reference(values[i])); |
322 } | 322 } |
323 return valueList; | 323 return valueList; |
324 } | 324 } |
325 } | 325 } |
OLD | NEW |