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.InternalCompilerException; | |
8 import com.google.dart.compiler.ast.DartArrayLiteral; | |
9 import com.google.dart.compiler.ast.DartBinaryExpression; | |
10 import com.google.dart.compiler.ast.DartExpression; | |
11 import com.google.dart.compiler.ast.DartFunction; | |
12 import com.google.dart.compiler.ast.DartFunctionObjectInvocation; | |
13 import com.google.dart.compiler.ast.DartIdentifier; | |
14 import com.google.dart.compiler.ast.DartInvocation; | |
15 import com.google.dart.compiler.ast.DartMapLiteral; | |
16 import com.google.dart.compiler.ast.DartMethodInvocation; | |
17 import com.google.dart.compiler.ast.DartNewExpression; | |
18 import com.google.dart.compiler.ast.DartNode; | |
19 import com.google.dart.compiler.ast.DartNodeTraverser; | |
20 import com.google.dart.compiler.ast.DartParenthesizedExpression; | |
21 import com.google.dart.compiler.ast.DartPropertyAccess; | |
22 import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; | |
23 import com.google.dart.compiler.ast.DartStringInterpolation; | |
24 import com.google.dart.compiler.ast.DartSuperExpression; | |
25 import com.google.dart.compiler.ast.DartThisExpression; | |
26 import com.google.dart.compiler.ast.DartUnaryExpression; | |
27 import com.google.dart.compiler.ast.DartUnqualifiedInvocation; | |
28 import com.google.dart.compiler.type.Type; | |
29 | |
30 /** | |
31 * Given an expression, Determines if the expression matches all the rules for a | |
32 * compile-time constant expression and emits a resolution error if not. | |
33 * | |
34 * This script doesn't just resolve expressions, it also sets types to the | |
35 * extent needed to validate compile-time constant expressions (boolean, int, | |
36 * double, and string types might be set) | |
37 * | |
38 */ | |
39 public class CompileTimeConstVisitor extends DartNodeTraverser<Void> { | |
40 | |
41 static CompileTimeConstVisitor create(CoreTypeProvider typeProvider, Resolutio
nContext context) { | |
42 return new CompileTimeConstVisitor(typeProvider, context); | |
43 } | |
44 | |
45 private final ResolutionContext context; | |
46 | |
47 private final Type boolType; | |
48 private final Type doubleType; | |
49 private final Type intType; | |
50 private final Type numType; | |
51 private final Type stringType; | |
52 private final Type dynamicType; | |
53 | |
54 | |
55 private CompileTimeConstVisitor(CoreTypeProvider typeProvider, ResolutionConte
xt context) { | |
56 this.context = context; | |
57 this.boolType = typeProvider.getBoolType(); | |
58 this.doubleType = typeProvider.getDoubleType(); | |
59 this.intType = typeProvider.getIntType(); | |
60 this.numType = typeProvider.getNumType(); | |
61 this.stringType = typeProvider.getStringType(); | |
62 this.dynamicType = typeProvider.getDynamicType(); | |
63 } | |
64 | |
65 private boolean checkBoolean(DartNode x, Type type) { | |
66 if (!type.equals(boolType)) { | |
67 context.onError(x, ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_BOOLEAN, | |
68 type.toString()); | |
69 return false; | |
70 } | |
71 return true; | |
72 } | |
73 | |
74 private boolean checkInt(DartNode x, Type type) { | |
75 if (!type.equals(intType)) { | |
76 context.onError(x, ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_
INT, | |
77 type.toString()); | |
78 return false; | |
79 } | |
80 return true; | |
81 } | |
82 | |
83 private boolean checkNumber(DartNode x, Type type) { | |
84 if (!(type.equals(numType) || type.equals(intType) || type.equals(doubleType
))) { | |
85 context.onError(x, ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_NUMBER, | |
86 type.toString()); | |
87 return false; | |
88 } | |
89 return true; | |
90 } | |
91 | |
92 private boolean checkNumberBooleanOrStringType(DartNode x, Type type) { | |
93 if (!type.equals(intType) && !type.equals(boolType) | |
94 && !type.equals(numType) && !type.equals(doubleType) && !type.equals(str
ingType)) { | |
95 context.onError(x, | |
96 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL, | |
97 type.toString()); | |
98 return false; | |
99 } | |
100 return true; | |
101 } | |
102 | |
103 @Override | |
104 public Void visitBinaryExpression(DartBinaryExpression x) { | |
105 x.visitChildren(this); | |
106 | |
107 DartExpression lhs = x.getArg1(); | |
108 DartExpression rhs = x.getArg2(); | |
109 Type lhsType = getMostSpecificType(lhs); | |
110 Type rhsType = getMostSpecificType(rhs); | |
111 lhsType.getClass(); // fast null check | |
112 rhsType.getClass(); // fast null check | |
113 switch (x.getOperator()) { | |
114 case NE: | |
115 case EQ: | |
116 case NE_STRICT: | |
117 case EQ_STRICT: | |
118 if (checkNumberBooleanOrStringType(lhs, lhsType) | |
119 && checkNumberBooleanOrStringType(rhs, rhsType)) { | |
120 setType(x, boolType); | |
121 } | |
122 break; | |
123 | |
124 case AND: | |
125 case OR: | |
126 if (checkBoolean(lhs, lhsType) && checkBoolean(rhs, rhsType)) { | |
127 setType(x, boolType); | |
128 } | |
129 break; | |
130 | |
131 case BIT_NOT: | |
132 case TRUNC: | |
133 case BIT_XOR: | |
134 case BIT_AND: | |
135 case BIT_OR: | |
136 case SAR: | |
137 case SHL: | |
138 if (checkInt(lhs, lhsType) && checkInt(rhs, rhsType)) { | |
139 setType(x, intType); | |
140 } | |
141 break; | |
142 | |
143 case ADD: | |
144 case SUB: | |
145 case MUL: | |
146 case DIV: | |
147 case MOD: | |
148 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
149 setType(x, numType); | |
150 } | |
151 break; | |
152 case LT: | |
153 case GT: | |
154 case LTE: | |
155 case GTE: | |
156 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
157 setType(x, boolType); | |
158 } | |
159 break; | |
160 | |
161 default: | |
162 // all other operators... | |
163 expectedConstant(x); | |
164 } | |
165 return null; | |
166 } | |
167 | |
168 @Override | |
169 public Void visitParenthesizedExpression(DartParenthesizedExpression x) { | |
170 x.visitChildren(this); | |
171 Type type = getMostSpecificType(x.getExpression()); | |
172 setType(x, type); | |
173 return null; | |
174 } | |
175 | |
176 @Override | |
177 public Void visitPropertyAccess(DartPropertyAccess x) { | |
178 x.visitChildren(this); | |
179 switch (ElementKind.of(x.getQualifier().getSymbol())) { | |
180 case CLASS: | |
181 case LIBRARY: | |
182 case NONE: | |
183 // OK. | |
184 break; | |
185 default: | |
186 expectedConstant(x); | |
187 return null; | |
188 } | |
189 Element element = x.getName().getSymbol(); | |
190 if (element != null && !element.getModifiers().isConstant()) { | |
191 expectedConstant(x); | |
192 } | |
193 Type type = getMostSpecificType(x.getName()); | |
194 setType(x, type); | |
195 return null; | |
196 } | |
197 | |
198 @Override | |
199 public Void visitRedirectConstructorInvocation(DartRedirectConstructorInvocati
on x) { | |
200 Element element = x.getSymbol(); | |
201 if (element != null) { | |
202 if (!element.getModifiers().isConstant()) { | |
203 expectedConstant(x); | |
204 } | |
205 } | |
206 x.visitChildren(this); | |
207 return null; | |
208 } | |
209 | |
210 @Override | |
211 public Void visitStringInterpolation(DartStringInterpolation x) { | |
212 expectedConstant(x); | |
213 return null; | |
214 } | |
215 | |
216 @Override | |
217 public Void visitSuperExpression(DartSuperExpression x) { | |
218 // No need to traverse further - super() expressions are never constant | |
219 expectedConstant(x); | |
220 return null; | |
221 } | |
222 | |
223 @Override | |
224 public Void visitUnaryExpression(DartUnaryExpression x) { | |
225 x.visitChildren(this); | |
226 | |
227 Type type = getMostSpecificType(x.getArg()); | |
228 switch (x.getOperator()) { | |
229 case NOT: | |
230 if (checkBoolean(x, type)) { | |
231 x.setType(boolType); | |
232 } | |
233 break; | |
234 case SUB: | |
235 if (checkNumber(x, type)) { | |
236 x.setType(numType); | |
237 } | |
238 break; | |
239 case BIT_NOT: | |
240 if (checkInt(x, type)) { | |
241 x.setType(intType); | |
242 } | |
243 break; | |
244 default: | |
245 expectedConstant(x); | |
246 } | |
247 return null; | |
248 } | |
249 | |
250 @Override | |
251 public Void visitArrayLiteral(DartArrayLiteral x) { | |
252 if (!x.isConst()) { | |
253 expectedConstant(x); | |
254 } else { | |
255 x.visitChildren(this); | |
256 } | |
257 return null; | |
258 } | |
259 | |
260 @Override | |
261 public Void visitFunction(DartFunction x) { | |
262 // No need to traverse, functions are always disallowed. | |
263 expectedConstant(x); | |
264 return null; | |
265 } | |
266 | |
267 @Override | |
268 public Void visitFunctionObjectInvocation(DartFunctionObjectInvocation x) { | |
269 // No need to traverse, function object invocations are always disallowed. | |
270 expectedConstant(x); | |
271 return null; | |
272 } | |
273 | |
274 @Override | |
275 public Void visitIdentifier(DartIdentifier x) { | |
276 x.visitChildren(this); | |
277 | |
278 Element element = x.getSymbol(); | |
279 switch(ElementKind.of(element)) { | |
280 case CLASS: | |
281 case PARAMETER: | |
282 // OK | |
283 break; | |
284 case FIELD: | |
285 case CONSTRUCTOR: | |
286 case VARIABLE: | |
287 if (!element.getModifiers().isConstant()) { | |
288 expectedConstant(x); | |
289 } else { | |
290 setType(x, getMostSpecificType(x)); | |
291 } | |
292 break; | |
293 | |
294 case NONE: | |
295 Type type = getMostSpecificType(x); | |
296 if (dynamicType.equals(type)) { | |
297 // TODO(zundel) This is the case for unresolved identifiers that need
to be recursively | |
298 // checked. | |
299 // expectedConstant(x); | |
300 } | |
301 setType(x, type); | |
302 break; | |
303 default: | |
304 throw new InternalCompilerException("Unexpected element " + x.toString() | |
305 + " kind: " + ElementKind.of(element) | |
306 + " evaluating type for compile-time constant expression."); | |
307 } | |
308 return null; | |
309 } | |
310 | |
311 | |
312 @Override | |
313 public Void visitInvocation(DartInvocation x) { | |
314 // No need to traverse, invocations are always disallowed. | |
315 expectedConstant(x); | |
316 return null; | |
317 } | |
318 | |
319 @Override | |
320 public Void visitMapLiteral(DartMapLiteral x) { | |
321 if (!x.isConst()) { | |
322 expectedConstant(x); | |
323 } else { | |
324 x.visitChildren(this); | |
325 } | |
326 return null; | |
327 } | |
328 | |
329 @Override | |
330 public Void visitMethodInvocation(DartMethodInvocation x) { | |
331 // No need to traverse, method invocations are always disallowed. | |
332 expectedConstant(x); | |
333 return null; | |
334 } | |
335 | |
336 @Override | |
337 public Void visitNewExpression(DartNewExpression x) { | |
338 if (!x.isConst()) { | |
339 expectedConstant(x); | |
340 } else { | |
341 x.visitChildren(this); | |
342 } | |
343 return null; | |
344 } | |
345 | |
346 | |
347 @Override | |
348 public Void visitThisExpression(DartThisExpression x) { | |
349 // No need to traverse, this expressions are always disallowed. | |
350 expectedConstant(x); | |
351 return null; | |
352 } | |
353 | |
354 @Override | |
355 public Void visitUnqualifiedInvocation(DartUnqualifiedInvocation x) { | |
356 // No need to traverse, always disallowed. | |
357 expectedConstant(x); | |
358 return null; | |
359 } | |
360 | |
361 /** | |
362 * Logs a general message "expected a constant expression" error. Use a more | |
363 * specific error message when possible. | |
364 */ | |
365 private void expectedConstant(DartNode x) { | |
366 context.onError(x, ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION); | |
367 } | |
368 | |
369 /** | |
370 * Determine the most specific type assigned to an expression node. Prefer the | |
371 * setting in the expression's symbol if present. Otherwise, use a type tagged | |
372 * in the expression node itself. | |
373 * | |
374 * @return a non <code>null</code> type value. Dynamic if none other can be | |
375 * determined. | |
376 */ | |
377 private Type getMostSpecificType(DartExpression expr) { | |
378 // TODO(zundel): this routine needs to recursively resolve as compile time c
onstants any | |
379 // symbols that have not yet been resolved. | |
380 Element element = (Element)expr.getSymbol(); | |
381 switch (ElementKind.of(element)) { | |
382 case FIELD: | |
383 return ((FieldElement)element).getType(); | |
384 case METHOD: | |
385 return ((MethodElement)element).getType(); | |
386 case VARIABLE: | |
387 return((VariableElement)element).getType(); | |
388 case CONSTRUCTOR: | |
389 return ((ConstructorElement)element).getType(); | |
390 case NONE: | |
391 if (expr.getType() != null) { | |
392 return expr.getType(); | |
393 } | |
394 return dynamicType; | |
395 default: | |
396 throw new InternalCompilerException("Unexpected element " + expr.toStrin
g() | |
397 + " kind: " + ElementKind.of(element
) | |
398 + "evaluating type for compile-time
constant expression."); | |
399 } | |
400 } | |
401 | |
402 private void setType(DartExpression x, Type type) { | |
403 Element element = (Element)x.getSymbol(); | |
404 if (element != null) { | |
405 Elements.setType(element, type); | |
406 } | |
407 // Also set on the expression node itself. Not every expression has a symbo
l. | |
408 x.setType(type); | |
409 } | |
410 } | |
OLD | NEW |