OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 library dart2js.constants.expressions; | |
6 | |
7 import '../dart2jslib.dart' show assertDebugMode; | |
8 import '../dart_types.dart'; | |
9 import '../elements/elements.dart' show | |
10 Element, | |
11 FunctionElement, | |
12 VariableElement; | |
13 import '../universe/universe.dart' show Selector; | |
14 import 'values.dart'; | |
15 | |
16 /// An expression that is a compile-time constant. | |
17 /// | |
18 /// Whereas [ConstantValue] represent a compile-time value, a | |
19 /// [ConstantExpression] represents an expression for creating a constant. | |
20 /// | |
21 /// There is no one-to-one mapping between [ConstantExpression] and | |
22 /// [ConstantValue], because different expressions can denote the same constant. | |
23 /// For instance, multiple `const` constructors may be used to create the same | |
24 /// object, and different `const` variables may hold the same value. | |
25 abstract class ConstantExpression { | |
26 /// Returns the value of this constant expression. | |
27 ConstantValue get value; | |
28 | |
29 // TODO(johnniwinther): Unify precedence handled between constants, front-end | |
30 // and back-end. | |
31 int get precedence => 16; | |
32 | |
33 accept(ConstantExpressionVisitor visitor); | |
34 | |
35 String getText() { | |
36 ConstExpPrinter printer = new ConstExpPrinter(); | |
37 accept(printer); | |
38 return printer.toString(); | |
39 } | |
40 | |
41 String toString() { | |
42 assertDebugMode('Use ConstantExpression.getText() instead of ' | |
43 'ConstantExpression.toString()'); | |
44 return getText(); | |
45 } | |
46 } | |
47 | |
48 /// Boolean, int, double, string, or null constant. | |
49 class PrimitiveConstantExpression extends ConstantExpression { | |
50 final PrimitiveConstantValue value; | |
51 | |
52 PrimitiveConstantExpression(this.value) { | |
53 assert(value != null); | |
54 } | |
55 | |
56 accept(ConstantExpressionVisitor visitor) => visitor.visitPrimitive(this); | |
57 } | |
58 | |
59 /// Literal list constant. | |
60 class ListConstantExpression extends ConstantExpression { | |
61 final ListConstantValue value; | |
62 final InterfaceType type; | |
63 final List<ConstantExpression> values; | |
64 | |
65 ListConstantExpression(this.value, this.type, this.values); | |
66 | |
67 accept(ConstantExpressionVisitor visitor) => visitor.visitList(this); | |
68 } | |
69 | |
70 /// Literal map constant. | |
71 class MapConstantExpression extends ConstantExpression { | |
72 final MapConstantValue value; | |
73 final InterfaceType type; | |
74 final List<ConstantExpression> keys; | |
75 final List<ConstantExpression> values; | |
76 | |
77 MapConstantExpression(this.value, this.type, this.keys, this.values); | |
78 | |
79 accept(ConstantExpressionVisitor visitor) => visitor.visitMap(this); | |
80 } | |
81 | |
82 /// Invocation of a const constructor. | |
83 class ConstructedConstantExpresssion extends ConstantExpression { | |
84 final ConstantValue value; | |
85 final InterfaceType type; | |
86 final FunctionElement target; | |
87 final Selector selector; | |
88 final List<ConstantExpression> arguments; | |
89 | |
90 ConstructedConstantExpresssion(this.value, | |
91 this.type, | |
92 this.target, | |
93 this.selector, | |
94 this.arguments) { | |
95 assert(type.element == target.enclosingClass); | |
96 } | |
97 | |
98 accept(ConstantExpressionVisitor visitor) => visitor.visitConstructor(this); | |
99 } | |
100 | |
101 /// String literal with juxtaposition and/or interpolations. | |
102 // TODO(johnniwinther): Do we need this? | |
103 class ConcatenateConstantExpression extends ConstantExpression { | |
104 final StringConstantValue value; | |
105 final List<ConstantExpression> arguments; | |
106 | |
107 ConcatenateConstantExpression(this.value, this.arguments); | |
108 | |
109 accept(ConstantExpressionVisitor visitor) => visitor.visitConcatenate(this); | |
110 } | |
111 | |
112 /// Symbol literal. | |
113 class SymbolConstantExpression extends ConstantExpression { | |
114 final ConstructedConstantValue value; | |
115 final String name; | |
116 | |
117 SymbolConstantExpression(this.value, this.name); | |
118 | |
119 accept(ConstantExpressionVisitor visitor) => visitor.visitSymbol(this); | |
120 } | |
121 | |
122 /// Type literal. | |
123 class TypeConstantExpression extends ConstantExpression { | |
124 final TypeConstantValue value; | |
125 /// Either [DynamicType] or a raw [GenericType]. | |
126 final DartType type; | |
127 | |
128 TypeConstantExpression(this.value, this.type) { | |
129 assert(type is GenericType || type is DynamicType); | |
130 } | |
131 | |
132 accept(ConstantExpressionVisitor visitor) => visitor.visitType(this); | |
133 } | |
134 | |
135 /// Reference to a constant local, top-level, or static variable. | |
136 class VariableConstantExpression extends ConstantExpression { | |
137 final ConstantValue value; | |
138 final VariableElement element; | |
139 | |
140 VariableConstantExpression(this.value, this.element); | |
141 | |
142 accept(ConstantExpressionVisitor visitor) => visitor.visitVariable(this); | |
143 } | |
144 | |
145 /// Reference to a top-level or static function. | |
146 class FunctionConstantExpression extends ConstantExpression { | |
147 final FunctionConstantValue value; | |
148 final FunctionElement element; | |
149 | |
150 FunctionConstantExpression(this.value, this.element); | |
151 | |
152 accept(ConstantExpressionVisitor visitor) => visitor.visitFunction(this); | |
153 } | |
154 | |
155 /// A constant binary expression like `a * b` or `identical(a, b)`. | |
156 class BinaryConstantExpression extends ConstantExpression { | |
157 final ConstantValue value; | |
158 final ConstantExpression left; | |
159 final String operator; | |
160 final ConstantExpression right; | |
161 | |
162 BinaryConstantExpression(this.value, this.left, this.operator, this.right) { | |
163 assert(PRECEDENCE_MAP[operator] != null); | |
164 } | |
165 | |
166 accept(ConstantExpressionVisitor visitor) => visitor.visitBinary(this); | |
167 | |
168 int get precedence => PRECEDENCE_MAP[operator]; | |
169 | |
170 static const Map<String, int> PRECEDENCE_MAP = const { | |
171 'identical': 15, | |
172 '==': 6, | |
173 '!=': 6, | |
174 '&&': 5, | |
175 '||': 4, | |
176 '^': 9, | |
177 '&': 10, | |
178 '|': 8, | |
179 '>>': 11, | |
180 '<<': 11, | |
181 '+': 12, | |
182 '-': 12, | |
183 '*': 13, | |
184 '/': 13, | |
185 '~/': 13, | |
186 '>': 7, | |
187 '<': 7, | |
188 '>=': 7, | |
189 '<=': 7, | |
190 '%': 13, | |
191 }; | |
192 } | |
193 | |
194 /// A unary constant expression like `-a`. | |
195 class UnaryConstantExpression extends ConstantExpression { | |
196 final ConstantValue value; | |
197 final String operator; | |
198 final ConstantExpression expression; | |
199 | |
200 UnaryConstantExpression(this.value, this.operator, this.expression) { | |
201 assert(PRECEDENCE_MAP[operator] != null); | |
202 } | |
203 | |
204 accept(ConstantExpressionVisitor visitor) => visitor.visitUnary(this); | |
205 | |
206 int get precedence => PRECEDENCE_MAP[operator]; | |
207 | |
208 static const Map<String, int> PRECEDENCE_MAP = const { | |
209 '!': 14, | |
210 '~': 14, | |
211 '-': 14, | |
212 }; | |
213 } | |
214 | |
215 /// A constant conditional expression like `a ? b : c`. | |
216 class ConditionalConstantExpression extends ConstantExpression { | |
217 final ConstantValue value; | |
218 final ConstantExpression condition; | |
219 final ConstantExpression trueExp; | |
220 final ConstantExpression falseExp; | |
221 | |
222 ConditionalConstantExpression(this.value, | |
223 this.condition, | |
224 this.trueExp, | |
225 this.falseExp); | |
226 | |
227 accept(ConstantExpressionVisitor visitor) => visitor.visitConditional(this); | |
228 | |
229 int get precedence => 3; | |
230 } | |
231 | |
232 abstract class ConstantExpressionVisitor<T> { | |
233 T visit(ConstantExpression constant) => constant.accept(this); | |
234 | |
235 T visitPrimitive(PrimitiveConstantExpression exp); | |
236 T visitList(ListConstantExpression exp); | |
237 T visitMap(MapConstantExpression exp); | |
238 T visitConstructor(ConstructedConstantExpresssion exp); | |
239 T visitConcatenate(ConcatenateConstantExpression exp); | |
240 T visitSymbol(SymbolConstantExpression exp); | |
241 T visitType(TypeConstantExpression exp); | |
242 T visitVariable(VariableConstantExpression exp); | |
243 T visitFunction(FunctionConstantExpression exp); | |
244 T visitBinary(BinaryConstantExpression exp); | |
245 T visitUnary(UnaryConstantExpression exp); | |
246 T visitConditional(ConditionalConstantExpression exp); | |
247 } | |
248 | |
249 /// Represents the declaration of a constant [element] with value [expression]. | |
250 // TODO(johnniwinther): Where does this class belong? | |
251 class ConstDeclaration { | |
252 final VariableElement element; | |
253 final ConstantExpression expression; | |
254 | |
255 ConstDeclaration(this.element, this.expression); | |
256 } | |
257 | |
258 class ConstExpPrinter extends ConstantExpressionVisitor { | |
259 final StringBuffer sb = new StringBuffer(); | |
260 | |
261 write(ConstantExpression parent, | |
262 ConstantExpression child, | |
263 {bool leftAssociative: true}) { | |
264 if (child.precedence < parent.precedence || | |
265 !leftAssociative && child.precedence == parent.precedence) { | |
266 sb.write('('); | |
267 child.accept(this); | |
268 sb.write(')'); | |
269 } else { | |
270 child.accept(this); | |
271 } | |
272 } | |
273 | |
274 writeTypeArguments(InterfaceType type) { | |
275 if (type.treatAsRaw) return; | |
276 sb.write('<'); | |
277 bool needsComma = false; | |
278 for (DartType value in type.typeArguments) { | |
279 if (needsComma) { | |
280 sb.write(', '); | |
281 } | |
282 sb.write(value); | |
283 needsComma = true; | |
284 } | |
285 sb.write('>'); | |
286 } | |
287 | |
288 visitPrimitive(PrimitiveConstantExpression exp) { | |
289 sb.write(exp.value.unparse()); | |
290 } | |
291 | |
292 visitList(ListConstantExpression exp) { | |
293 sb.write('const '); | |
294 writeTypeArguments(exp.type); | |
295 sb.write('['); | |
296 bool needsComma = false; | |
297 for (ConstantExpression value in exp.values) { | |
298 if (needsComma) { | |
299 sb.write(', '); | |
300 } | |
301 visit(value); | |
302 needsComma = true; | |
303 } | |
304 sb.write(']'); | |
305 } | |
306 | |
307 visitMap(MapConstantExpression exp) { | |
308 sb.write('const '); | |
309 writeTypeArguments(exp.type); | |
310 sb.write('{'); | |
311 for (int index = 0; index < exp.keys.length; index++) { | |
312 if (index > 0) { | |
313 sb.write(', '); | |
314 } | |
315 visit(exp.keys[index]); | |
316 sb.write(': '); | |
317 visit(exp.values[index]); | |
318 } | |
319 sb.write('}'); | |
320 } | |
321 | |
322 visitConstructor(ConstructedConstantExpresssion exp) { | |
323 sb.write('const '); | |
324 sb.write(exp.target.enclosingClass.name); | |
325 if (exp.target.name != '') { | |
326 sb.write('.'); | |
327 sb.write(exp.target.name); | |
328 } | |
329 writeTypeArguments(exp.type); | |
330 sb.write('('); | |
331 bool needsComma = false; | |
332 | |
333 int namedOffset = exp.selector.positionalArgumentCount; | |
334 for (int index = 0; index < namedOffset; index++) { | |
335 if (needsComma) { | |
336 sb.write(', '); | |
337 } | |
338 visit(exp.arguments[index]); | |
339 needsComma = true; | |
340 } | |
341 for (int index = 0; index < exp.selector.namedArgumentCount; index++) { | |
342 if (needsComma) { | |
343 sb.write(', '); | |
344 } | |
345 sb.write(exp.selector.namedArguments[index]); | |
346 sb.write(': '); | |
347 visit(exp.arguments[namedOffset + index]); | |
348 needsComma = true; | |
349 } | |
350 sb.write(')'); | |
351 } | |
352 | |
353 visitConcatenate(ConcatenateConstantExpression exp) { | |
354 sb.write(exp.value.unparse()); | |
355 } | |
356 | |
357 visitSymbol(SymbolConstantExpression exp) { | |
358 sb.write('#'); | |
359 sb.write(exp.name); | |
360 } | |
361 | |
362 visitType(TypeConstantExpression exp) { | |
363 sb.write(exp.type.name); | |
364 } | |
365 | |
366 visitVariable(VariableConstantExpression exp) { | |
367 if (exp.element.isStatic) { | |
368 sb.write(exp.element.enclosingClass.name); | |
369 sb.write('.'); | |
370 } | |
371 sb.write(exp.element.name); | |
372 } | |
373 | |
374 visitFunction(FunctionConstantExpression exp) { | |
375 if (exp.element.isStatic) { | |
376 sb.write(exp.element.enclosingClass.name); | |
377 sb.write('.'); | |
378 } | |
379 sb.write(exp.element.name); | |
380 } | |
381 | |
382 visitBinary(BinaryConstantExpression exp) { | |
383 if (exp.operator == 'identical') { | |
384 sb.write('identical('); | |
385 visit(exp.left); | |
386 sb.write(', '); | |
387 visit(exp.right); | |
388 sb.write(')'); | |
389 } else { | |
390 write(exp, exp.left); | |
391 sb.write(' '); | |
392 sb.write(exp.operator); | |
393 sb.write(' '); | |
394 write(exp, exp.right); | |
395 } | |
396 } | |
397 | |
398 visitUnary(UnaryConstantExpression exp) { | |
399 sb.write(exp.operator); | |
400 write(exp, exp.expression); | |
401 } | |
402 | |
403 visitConditional(ConditionalConstantExpression exp) { | |
404 write(exp, exp.condition, leftAssociative: false); | |
405 sb.write(' ? '); | |
406 write(exp, exp.trueExp); | |
407 sb.write(' : '); | |
408 write(exp, exp.falseExp); | |
409 } | |
410 | |
411 String toString() => sb.toString(); | |
412 } | |
OLD | NEW |