OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011, 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 package com.google.dart.compiler.resolver; | |
6 | |
7 import com.google.dart.compiler.DartCompilerErrorCode; | |
8 import com.google.dart.compiler.ast.DartArrayAccess; | |
9 import com.google.dart.compiler.ast.DartArrayLiteral; | |
10 import com.google.dart.compiler.ast.DartBinaryExpression; | |
11 import com.google.dart.compiler.ast.DartContext; | |
12 import com.google.dart.compiler.ast.DartExpression; | |
13 import com.google.dart.compiler.ast.DartFunction; | |
14 import com.google.dart.compiler.ast.DartFunctionObjectInvocation; | |
15 import com.google.dart.compiler.ast.DartIdentifier; | |
16 import com.google.dart.compiler.ast.DartIntegerLiteral; | |
17 import com.google.dart.compiler.ast.DartInvocation; | |
18 import com.google.dart.compiler.ast.DartMapLiteral; | |
19 import com.google.dart.compiler.ast.DartMethodInvocation; | |
20 import com.google.dart.compiler.ast.DartNewExpression; | |
21 import com.google.dart.compiler.ast.DartNode; | |
22 import com.google.dart.compiler.ast.DartNullLiteral; | |
23 import com.google.dart.compiler.ast.DartParenthesizedExpression; | |
24 import com.google.dart.compiler.ast.DartPropertyAccess; | |
25 import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; | |
26 import com.google.dart.compiler.ast.DartStringInterpolation; | |
27 import com.google.dart.compiler.ast.DartStringLiteral; | |
28 import com.google.dart.compiler.ast.DartSuperExpression; | |
29 import com.google.dart.compiler.ast.DartThisExpression; | |
30 import com.google.dart.compiler.ast.DartUnaryExpression; | |
31 import com.google.dart.compiler.ast.DartUnqualifiedInvocation; | |
32 import com.google.dart.compiler.ast.DartVisitor; | |
33 import com.google.dart.compiler.type.Type; | |
34 | |
35 /** | |
36 * Given an expression, Determines if the expression matches all the rules for a | |
37 * compile-time constant expression and emits a resolution error if not. | |
38 * | |
39 * This script doesn't just resolve expressions, it also sets types to the | |
40 * extent needed to validate compile-time constant expressions (boolean, int, | |
41 * double, and string types might be set) | |
42 * | |
43 */ | |
44 public class CompileTimeConstVisitor extends DartVisitor { | |
ngeoffray
2011/10/14 09:26:56
Please use the DartNodeTraverser instead. I think
zundel
2011/10/14 20:59:52
Done.
| |
45 | |
46 static CompileTimeConstVisitor create(CoreTypeProvider typeProvider, Resolutio nContext context) { | |
47 return new CompileTimeConstVisitor(typeProvider, context); | |
48 } | |
49 | |
50 private final ResolutionContext context; | |
51 | |
52 private final Type boolType; | |
53 private final Type doubleType; | |
54 private final Type intType; | |
55 private final Type numType; | |
56 private final Type stringType; | |
57 private final Type dynamicType; | |
58 | |
59 | |
60 private CompileTimeConstVisitor(CoreTypeProvider typeProvider, ResolutionConte xt context) { | |
61 this.context = context; | |
62 this.boolType = typeProvider.getBoolType(); | |
63 this.doubleType = typeProvider.getDoubleType(); | |
64 this.intType = typeProvider.getIntType(); | |
65 this.numType = typeProvider.getNumType(); | |
66 this.stringType = typeProvider.getStringType(); | |
67 this.dynamicType = typeProvider.getDynamicType(); | |
68 } | |
69 | |
70 private boolean checkBoolean(DartNode x, Type type) { | |
71 if (!type.equals(boolType)) { | |
72 context.resolutionError(x, DartCompilerErrorCode.EXPECTED_CONSTANT_EXPRESS ION_BOOLEAN, | |
73 type.toString()); | |
74 return false; | |
75 } | |
76 return true; | |
77 } | |
78 | |
79 private boolean checkInt(DartNode x, Type type) { | |
80 if (!type.equals(intType)) { | |
81 context.resolutionError(x, DartCompilerErrorCode.EXPECTED_CONSTANT_EXPRESS ION_INT, | |
82 type.toString()); | |
83 return false; | |
84 } | |
85 return true; | |
86 } | |
87 | |
88 private boolean checkNumber(DartNode x, Type type) { | |
89 if (!(type.equals(numType) || type.equals(intType) || type.equals(doubleType ))) { | |
90 context.resolutionError(x, DartCompilerErrorCode.EXPECTED_CONSTANT_EXPRESS ION_NUMBER, | |
91 type.toString()); | |
92 return false; | |
93 } | |
94 return true; | |
95 } | |
96 | |
97 private boolean checkNumberBooleanOrStringType(DartNode x, Type type) { | |
98 if (!type.equals(intType) && !type.equals(boolType) | |
99 && !type.equals(numType) && !type.equals(doubleType) && !type.equals(str ingType)) { | |
100 context.resolutionError(x, | |
101 DartCompilerErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL, | |
102 type.toString()); | |
103 return false; | |
104 } | |
105 return true; | |
106 } | |
107 | |
108 @Override | |
109 public void endVisit(DartBinaryExpression x, DartContext ctx) { | |
110 DartExpression lhs = x.getArg1(); | |
111 DartExpression rhs = x.getArg2(); | |
112 Type lhsType = getMostSpecificType(lhs); | |
113 Type rhsType = getMostSpecificType(rhs); | |
114 lhsType.getClass(); // fast null check | |
115 rhsType.getClass(); // fast null check | |
ngeoffray
2011/10/14 09:26:56
you're doing the null checks and then checking for
zundel
2011/10/14 20:59:52
Done.
| |
116 boolean isConst = true; | |
ngeoffray
2011/10/14 09:26:56
No need for this variable if lhsType and rhsType c
zundel
2011/10/14 20:59:52
Done.
| |
117 if (lhsType == null || rhsType == null) { | |
118 isConst = false; | |
119 } | |
120 if (isConst) { | |
121 switch (x.getOperator()) { | |
122 case NE: | |
123 case EQ: | |
124 case NE_STRICT: | |
125 case EQ_STRICT: | |
126 if (checkNumberBooleanOrStringType(lhs, lhsType) && | |
ngeoffray
2011/10/14 09:26:56
Nit: && should be on a newline, right below check.
zundel
2011/10/14 20:59:52
Done.
| |
127 checkNumberBooleanOrStringType(rhs, rhsType)) { | |
128 setType(x, boolType); | |
129 } | |
130 break; | |
131 | |
132 case AND: | |
133 case OR: | |
134 if (checkBoolean(lhs, lhsType) | |
135 && checkBoolean(rhs, rhsType)) { | |
ngeoffray
2011/10/14 09:26:56
weird indentation.
zundel
2011/10/14 20:59:52
Done.
| |
136 setType(x, boolType); | |
137 } | |
138 break; | |
139 | |
140 case BIT_NOT: | |
141 case TRUNC: | |
142 case BIT_XOR: | |
143 case BIT_AND: | |
144 case BIT_OR: | |
145 case SAR: | |
146 case SHL: | |
147 if (checkInt(lhs, lhsType) && | |
148 checkInt(rhs, rhsType)) { | |
149 setType(x, intType); | |
150 } | |
ngeoffray
2011/10/14 09:26:56
ditto.
zundel
2011/10/14 20:59:52
Done.
| |
151 break; | |
152 | |
153 case ADD: | |
154 case SUB: | |
155 case MUL: | |
156 case DIV: | |
157 case MOD: | |
158 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
159 setType(x, numType); | |
160 } | |
161 break; | |
162 case LT: | |
163 case GT: | |
164 case LTE: | |
165 case GTE: | |
166 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
167 setType(x, boolType); | |
168 } | |
169 break; | |
170 | |
171 default: | |
172 // all other operators... | |
173 expectedConstant(x); | |
174 } | |
175 } | |
176 } | |
177 | |
178 @Override | |
179 public void endVisit(DartParenthesizedExpression x, DartContext ctx) { | |
180 Type type = getMostSpecificType(x.getExpression()); | |
181 setType(x, type); | |
182 } | |
183 | |
184 @Override | |
185 public void endVisit(DartPropertyAccess x, DartContext ctx) { | |
floitsch
2011/10/14 14:36:58
Does this make sure that PropertyAccess only goes
zundel
2011/10/14 20:59:52
Done.
| |
186 Element element = x.getName().getSymbol(); | |
187 if (element != null && !element.getModifiers().isConstant()) { | |
188 expectedConstant(x); | |
189 } | |
190 Type type = getMostSpecificType(x.getName()); | |
191 setType(x, type); | |
192 } | |
193 | |
194 @Override | |
195 public boolean visit(DartRedirectConstructorInvocation x, DartContext ctx) { | |
196 if (!x.getSymbol().getModifiers().isConstant()) { | |
197 expectedConstant(x); | |
198 } | |
199 return true; | |
200 } | |
201 | |
202 @Override | |
203 public boolean visit(DartStringInterpolation x, DartContext ctx) { | |
204 expectedConstant(x); | |
205 return false; | |
206 } | |
207 | |
208 @Override | |
209 public boolean visit(DartStringLiteral x, DartContext ctx) { | |
210 return false; | |
floitsch
2011/10/14 14:36:58
I'm not really familiar with the visitor, but why
zundel
2011/10/14 20:59:52
returns false to stop traversing children.
I swit
| |
211 } | |
212 | |
213 @Override | |
214 public boolean visit(DartSuperExpression x, DartContext ctx) { | |
215 expectedConstant(x); | |
216 return false; | |
217 } | |
218 | |
219 @Override | |
220 public void endVisit(DartUnaryExpression x, DartContext ctx) { | |
221 Type type = getMostSpecificType(x.getArg()); | |
222 switch (x.getOperator()) { | |
223 case NOT: | |
224 if (checkBoolean(x, type)) { | |
225 x.setType(boolType); | |
226 } | |
227 break; | |
228 case SUB: | |
229 if (checkNumber(x, type)) { | |
230 x.setType(numType); | |
231 } | |
232 break; | |
233 case BIT_NOT: | |
234 if (checkInt(x, type)) { | |
235 x.setType(intType); | |
236 } | |
237 break; | |
238 default: | |
239 expectedConstant(x); | |
240 } | |
241 } | |
242 | |
243 | |
244 private void expectedConstant(DartNode x) { | |
245 context.resolutionError(x, DartCompilerErrorCode.EXPECTED_CONSTANT_EXPRESSIO N); | |
246 } | |
247 | |
248 /** | |
249 * Determine the most specific type assigned to an expression node. Prefer the | |
250 * setting in the expression's symbol if present. Otherwise, use a type tagged | |
251 * in the expression node itself. | |
252 * | |
253 * @return a non <code>null</code> type value. Dynamic if none other can be | |
254 * determined. | |
255 */ | |
256 private Type getMostSpecificType(DartExpression expr) { | |
257 Element element = (Element)expr.getSymbol(); | |
258 switch (ElementKind.of(element)) { | |
259 case FIELD: | |
260 return ((FieldElement)element).getType(); | |
261 case METHOD: | |
262 return ((MethodElement)element).getType(); | |
263 case VARIABLE: | |
264 return((VariableElement)element).getType(); | |
265 } | |
ngeoffray
2011/10/14 09:26:56
Maybe inline the following code into a 'default' c
zundel
2011/10/14 20:59:52
Done.
| |
266 | |
267 if (expr.getType() != null) { | |
268 return expr.getType(); | |
269 } | |
270 | |
271 return dynamicType; | |
272 } | |
273 | |
274 private void setType(DartExpression x, Type type) { | |
275 Element element = (Element)x.getSymbol(); | |
276 if (element != null) { | |
277 Elements.setType(element, type); | |
278 } | |
279 // Also set on the expression node itself. Not every expression has a symbo l. | |
280 x.setType(type); | |
281 } | |
282 | |
283 @Override | |
284 public boolean visit(DartArrayAccess x, DartContext ctx) { | |
285 // TODO(zundel): remove me if there is nothing to do | |
286 return true; | |
287 } | |
288 | |
289 @Override | |
290 public boolean visit(DartArrayLiteral x, DartContext ctx) { | |
291 if (!x.isConst()) { | |
292 expectedConstant(x); | |
293 } | |
294 return true; | |
295 } | |
296 | |
297 @Override | |
298 public boolean visit(DartFunction x, DartContext ctx) { | |
299 expectedConstant(x); | |
300 return true; | |
301 } | |
302 | |
303 @Override | |
304 public boolean visit(DartFunctionObjectInvocation x, DartContext ctx) { | |
305 expectedConstant(x); | |
306 return false; | |
307 } | |
308 | |
309 @Override | |
310 public boolean visit(DartIdentifier x, DartContext ctx) { | |
311 Element element = x.getSymbol(); | |
312 switch(ElementKind.of(element)) { | |
313 case FIELD: | |
314 case CONSTRUCTOR: | |
315 case VARIABLE: | |
316 case PARAMETER: | |
317 if (!element.getModifiers().isConstant()) { | |
318 expectedConstant(x); | |
319 } else { | |
320 setType(x, getMostSpecificType(x)); | |
321 } | |
322 break; | |
323 default: | |
324 } | |
325 return true; | |
326 } | |
327 | |
328 @Override | |
329 public boolean visit(DartIntegerLiteral x, DartContext ctx) { | |
330 return false; | |
331 } | |
332 | |
333 @Override | |
334 public boolean visit(DartInvocation x, DartContext ctx) { | |
335 expectedConstant(x); | |
336 return false; // No need to check further | |
337 } | |
338 | |
339 @Override | |
340 public boolean visit(DartMapLiteral x, DartContext ctx) { | |
341 if (!x.isConst()) { | |
342 expectedConstant(x); | |
343 } | |
344 return true; | |
345 } | |
346 | |
347 @Override | |
348 public boolean visit(DartMethodInvocation x, DartContext ctx) { | |
349 expectedConstant(x); | |
350 return false; | |
351 } | |
352 | |
353 @Override | |
354 public boolean visit(DartNewExpression x, DartContext ctx) { | |
355 boolean isConst = false; | |
356 Element element = x.getSymbol(); | |
357 switch (ElementKind.of(element)) { | |
ngeoffray
2011/10/14 09:26:56
Please add a comment that factory methods cannot b
zundel
2011/10/14 20:59:52
I'm keying off of the 'const' reserved word now, s
| |
358 case CONSTRUCTOR: | |
359 ConstructorElement constructorElement = (ConstructorElement) element; | |
360 if (constructorElement.getModifiers().isConstant()) { | |
361 isConst = true; | |
362 } | |
363 break; | |
364 } | |
365 if (!isConst) { | |
366 expectedConstant(x); | |
367 } | |
368 return true; | |
369 } | |
370 | |
371 @Override | |
372 public boolean visit(DartNullLiteral x, DartContext ctx) { | |
373 return true; | |
374 } | |
375 | |
376 @Override | |
377 public boolean visit(DartThisExpression x, DartContext ctx) { | |
378 expectedConstant(x); | |
379 return true; | |
380 } | |
381 | |
382 @Override | |
383 public boolean visit(DartUnqualifiedInvocation x, DartContext ctx) { | |
384 expectedConstant(x); | |
385 return false; | |
386 } | |
387 } | |
388 | |
OLD | NEW |