Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js_backend/constant_emitter.dart

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

Powered by Google App Engine
This is Rietveld 408576698