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

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

Powered by Google App Engine
This is Rietveld 408576698