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.common.collect.Lists; | |
8 import com.google.common.collect.Maps; | |
9 import com.google.common.collect.Sets; | |
10 import com.google.dart.compiler.DartCompilationError; | |
11 import com.google.dart.compiler.DartCompilationPhase; | |
12 import com.google.dart.compiler.DartCompilerContext; | |
13 import com.google.dart.compiler.InternalCompilerException; | |
14 import com.google.dart.compiler.ast.DartArrayLiteral; | |
15 import com.google.dart.compiler.ast.DartBinaryExpression; | |
16 import com.google.dart.compiler.ast.DartBooleanLiteral; | |
17 import com.google.dart.compiler.ast.DartDoubleLiteral; | |
18 import com.google.dart.compiler.ast.DartExpression; | |
19 import com.google.dart.compiler.ast.DartField; | |
20 import com.google.dart.compiler.ast.DartFunction; | |
21 import com.google.dart.compiler.ast.DartFunctionObjectInvocation; | |
22 import com.google.dart.compiler.ast.DartIdentifier; | |
23 import com.google.dart.compiler.ast.DartIntegerLiteral; | |
24 import com.google.dart.compiler.ast.DartInvocation; | |
25 import com.google.dart.compiler.ast.DartMapLiteral; | |
26 import com.google.dart.compiler.ast.DartMapLiteralEntry; | |
27 import com.google.dart.compiler.ast.DartMethodDefinition; | |
28 import com.google.dart.compiler.ast.DartMethodInvocation; | |
29 import com.google.dart.compiler.ast.DartNewExpression; | |
30 import com.google.dart.compiler.ast.DartNode; | |
31 import com.google.dart.compiler.ast.DartNodeTraverser; | |
32 import com.google.dart.compiler.ast.DartParameter; | |
33 import com.google.dart.compiler.ast.DartParenthesizedExpression; | |
34 import com.google.dart.compiler.ast.DartPropertyAccess; | |
35 import com.google.dart.compiler.ast.DartRedirectConstructorInvocation; | |
36 import com.google.dart.compiler.ast.DartStringInterpolation; | |
37 import com.google.dart.compiler.ast.DartStringLiteral; | |
38 import com.google.dart.compiler.ast.DartSuperExpression; | |
39 import com.google.dart.compiler.ast.DartThisExpression; | |
40 import com.google.dart.compiler.ast.DartUnaryExpression; | |
41 import com.google.dart.compiler.ast.DartUnit; | |
42 import com.google.dart.compiler.ast.DartUnqualifiedInvocation; | |
43 import com.google.dart.compiler.ast.DartVariable; | |
44 import com.google.dart.compiler.ast.DartVariableStatement; | |
45 import com.google.dart.compiler.ast.Modifiers; | |
46 import com.google.dart.compiler.common.Symbol; | |
47 import com.google.dart.compiler.type.Type; | |
48 | |
49 import java.util.List; | |
50 import java.util.Map; | |
51 import java.util.Set; | |
52 | |
53 /** | |
54 * Given an expression, Determines if the expression matches all the rules for a | |
mmendez
2011/11/17 15:32:10
Nit: Determines -> determines
| |
55 * compile-time constant expression and emits a resolution error if not. | |
56 * | |
57 * This script doesn't just resolve expressions, it also sets types to the | |
58 * extent needed to validate compile-time constant expressions (boolean, int, | |
59 * double, and string types might be set) | |
60 * | |
61 */ | |
62 public class CompileTimeConstantAnalyzer extends DartNodeTraverser<Void> { | |
mmendez
2011/11/17 15:32:10
Nit: not sure if you meant for this to be a visito
zundel
2011/11/17 16:26:42
Created a nested class.
| |
63 | |
64 public static class Phase implements DartCompilationPhase { | |
65 /** | |
66 * Executes symbol resolution on the given compilation unit. | |
67 * | |
68 * @param context The listener through which compilation errors are reported | |
69 * (not <code>null</code>) | |
70 */ | |
71 @Override | |
72 public DartUnit exec(DartUnit unit, DartCompilerContext context, | |
73 CoreTypeProvider typeProvider) { | |
74 new CompileTimeConstantAnalyzer(typeProvider, context).exec(unit); | |
75 return unit; | |
76 } | |
77 } | |
78 private class ExpressionVisitor extends DartNodeTraverser<Void> { | |
79 private ExpressionVisitor() { | |
80 } | |
81 | |
82 private boolean checkBoolean(DartNode x, Type type) { | |
83 if (!type.equals(boolType)) { | |
84 context.onError(new DartCompilationError(x, | |
85 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_BOOLEAN, type | |
86 .toString())); | |
87 return false; | |
88 } | |
89 return true; | |
90 } | |
91 | |
92 private boolean checkInt(DartNode x, Type type) { | |
93 if (!type.equals(intType)) { | |
94 context | |
95 .onError(new DartCompilationError(x, | |
96 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_INT, type | |
97 .toString())); | |
98 return false; | |
99 } | |
100 return true; | |
101 } | |
102 | |
103 private boolean checkNumber(DartNode x, Type type) { | |
104 if (!(type.equals(numType) || type.equals(intType) || type | |
105 .equals(doubleType))) { | |
106 context.onError(new DartCompilationError(x, | |
107 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_NUMBER, type | |
108 .toString())); | |
109 | |
110 return false; | |
111 } | |
112 return true; | |
113 } | |
114 | |
115 private boolean checkNumberBooleanOrStringType(DartNode x, Type type) { | |
116 if (!type.equals(intType) && !type.equals(boolType) | |
117 && !type.equals(numType) && !type.equals(doubleType) | |
118 && !type.equals(stringType)) { | |
119 context.onError(new DartCompilationError(x, | |
120 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL, | |
121 type.toString())); | |
122 return false; | |
123 } | |
124 return true; | |
125 } | |
126 | |
127 /** | |
128 * Logs a general message "expected a constant expression" error. Use a more | |
129 * specific error message when possible. | |
130 */ | |
131 private void expectedConstant(DartNode x) { | |
132 context.onError(new DartCompilationError(x, | |
133 ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION)); | |
134 } | |
135 | |
136 /** | |
137 * Determine the most specific type assigned to an expression node. Prefer | |
138 * the setting in the expression's symbol if present. Otherwise, use a type | |
139 * tagged in the expression node itself. | |
140 * | |
141 * @return a non <code>null</code> type value. Dynamic if none other can be | |
142 * determined. | |
143 */ | |
144 private Type getMostSpecificType(DartNode node) { | |
145 Element element = (Element) node.getSymbol(); | |
146 Type type = inferredTypes.get(node); | |
147 if (type != null) { | |
148 return type; | |
149 } | |
150 | |
151 switch (ElementKind.of(element)) { | |
mmendez
2011/11/17 15:32:10
Can't you have the FIELD, METHOD, VARIABLE and CON
zundel
2011/11/17 16:26:42
Done.
| |
152 case FIELD: | |
153 type = ((FieldElement) element).getType(); | |
154 break; | |
155 case METHOD: | |
156 type = ((MethodElement) element).getType(); | |
157 break; | |
158 case VARIABLE: | |
159 type = ((VariableElement) element).getType(); | |
160 break; | |
161 case CONSTRUCTOR: | |
162 type = ((ConstructorElement) element).getType(); | |
163 break; | |
164 case NONE: | |
165 if (node.getType() != null) { | |
166 return node.getType(); | |
167 } | |
168 return dynamicType; | |
169 default: | |
170 throw new InternalCompilerException("Unexpected element " | |
mmendez
2011/11/17 15:32:10
I think that this will ICE if you access a constan
zundel
2011/11/17 16:26:42
I don't think so. that happens all the time in the
| |
171 + node.toString() + " kind: " + ElementKind.of(element) | |
172 + "evaluating type for compile-time constant expression."); | |
173 } | |
174 if (type == null) { | |
175 return node.getType(); | |
mmendez
2011/11/17 15:32:10
Could you add a comment explaining under which cir
zundel
2011/11/17 16:26:42
It was happening when diet parsed files didn't hav
| |
176 } | |
177 return type; | |
178 } | |
179 | |
180 private void setType(DartNode x, Type type) { | |
mmendez
2011/11/17 15:32:10
Rename to cache type for node? It doesn't actuall
zundel
2011/11/17 16:26:42
renamed
| |
181 if (type != null && ! type.equals(dynamicType)) { | |
182 inferredTypes.put(x, type); | |
183 } | |
184 } | |
185 | |
186 @Override | |
187 public Void visitArrayLiteral(DartArrayLiteral x) { | |
188 if (!x.isConst()) { | |
189 expectedConstant(x); | |
190 } else { | |
191 for (DartExpression expr : x.getExpressions()) { | |
192 expr.accept(this); | |
193 } | |
194 } | |
195 return null; | |
196 } | |
197 | |
198 @Override | |
199 public Void visitBinaryExpression(DartBinaryExpression x) { | |
200 x.visitChildren(this); | |
201 | |
202 DartExpression lhs = x.getArg1(); | |
203 DartExpression rhs = x.getArg2(); | |
204 Type lhsType = getMostSpecificType(lhs); | |
205 Type rhsType = getMostSpecificType(rhs); | |
206 if (lhsType == null) { | |
207 lhsType = dynamicType; | |
208 } | |
209 if (rhsType == null) { | |
210 rhsType = dynamicType; | |
211 } | |
212 | |
213 switch (x.getOperator()) { | |
214 case NE: | |
215 case EQ: | |
216 case NE_STRICT: | |
217 case EQ_STRICT: | |
218 if (checkNumberBooleanOrStringType(lhs, lhsType) | |
219 && checkNumberBooleanOrStringType(rhs, rhsType)) { | |
220 setType(x, boolType); | |
221 } | |
222 break; | |
223 | |
224 case AND: | |
225 case OR: | |
226 if (checkBoolean(lhs, lhsType) && checkBoolean(rhs, rhsType)) { | |
227 setType(x, boolType); | |
228 } | |
229 break; | |
230 | |
231 case BIT_NOT: | |
232 case TRUNC: | |
233 case BIT_XOR: | |
234 case BIT_AND: | |
235 case BIT_OR: | |
236 case SAR: | |
237 case SHL: | |
238 if (checkInt(lhs, lhsType) && checkInt(rhs, rhsType)) { | |
239 setType(x, intType); | |
240 } | |
241 break; | |
242 | |
243 case ADD: | |
244 case SUB: | |
245 case MUL: | |
246 case DIV: | |
247 case MOD: | |
248 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
249 setType(x, numType); | |
250 } | |
251 break; | |
252 case LT: | |
253 case GT: | |
254 case LTE: | |
255 case GTE: | |
256 if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) { | |
257 setType(x, boolType); | |
258 } | |
259 break; | |
260 | |
261 default: | |
262 // all other operators... | |
263 expectedConstant(x); | |
264 } | |
265 return null; | |
266 } | |
267 | |
268 @Override | |
269 public Void visitBooleanLiteral(DartBooleanLiteral x) { | |
mmendez
2011/11/17 15:32:10
Nit: Not part of your patch, but I would have a ex
zundel
2011/11/17 16:26:42
Doesn't get set until Resolver is run. That is ne
| |
270 setType(x, boolType); | |
271 return null; | |
272 } | |
273 | |
274 @Override | |
275 public Void visitDoubleLiteral(DartDoubleLiteral x) { | |
276 setType(x, doubleType); | |
277 return null; | |
278 } | |
279 | |
280 @Override | |
281 public Void visitField(DartField x) { | |
282 x.visitChildren(this); | |
283 if (x.getType() == null || x.getType().equals(dynamicType)) { | |
284 Type type = getMostSpecificType(x.getValue()); | |
285 setType(x, type); | |
286 } | |
287 return null; | |
288 } | |
289 | |
290 @Override | |
291 public Void visitFunction(DartFunction x) { | |
292 // No need to traverse, functions are always disallowed. | |
293 expectedConstant(x); | |
294 return null; | |
295 } | |
296 | |
297 @Override | |
298 public Void visitFunctionObjectInvocation(DartFunctionObjectInvocation x) { | |
299 // No need to traverse, function object invocations are always disallowed. | |
300 expectedConstant(x); | |
301 return null; | |
302 } | |
303 | |
304 @Override | |
305 public Void visitIdentifier(DartIdentifier x) { | |
306 x.visitChildren(this); | |
307 | |
308 Element element = x.getSymbol(); | |
309 switch (ElementKind.of(element)) { | |
310 case CLASS: | |
311 case PARAMETER: | |
312 // OK | |
313 break; | |
314 case FIELD: | |
315 case VARIABLE: | |
316 | |
317 if (element != null && visitedSymbols.contains(element)) { | |
318 context.onError(new DartCompilationError(x, | |
319 ResolverErrorCode.CIRCULAR_REFERENCE)); | |
320 setType(x, getMostSpecificType(x)); | |
321 return null; | |
322 } | |
323 visitedSymbols.add(element); | |
324 | |
325 if (!element.getModifiers().isConstant()) { | |
326 expectedConstant(x); | |
327 } | |
328 DartNode identifierNode = element.getNode(); | |
329 | |
330 switch (ElementKind.of(element)) { | |
331 case FIELD: | |
332 setType(x, getMostSpecificType(x)); | |
333 break; | |
334 } | |
335 this.visit(Lists.newArrayList(identifierNode)); | |
336 | |
337 visitedSymbols.remove(element); | |
338 | |
339 switch (ElementKind.of(element)) { | |
mmendez
2011/11/17 15:32:10
Won't this reset the value set on line 332? Maybe
zundel
2011/11/17 16:26:42
There is a tree walk in the middle. The type was
| |
340 case FIELD: | |
341 setType(x, getMostSpecificType(identifierNode)); | |
342 break; | |
343 } | |
344 | |
345 break; | |
346 | |
347 case CONSTRUCTOR: | |
348 if (!element.getModifiers().isConstant()) { | |
349 expectedConstant(x); | |
350 } | |
351 setType(x, getMostSpecificType(x)); | |
352 break; | |
353 | |
354 case NONE: | |
355 Type type = getMostSpecificType(x); | |
356 if (dynamicType.equals(type)) { | |
357 // TODO(zundel) This is the case for unresolved identifiers that | |
358 // need to be recursively | |
359 // checked. | |
360 expectedConstant(x); | |
361 } | |
362 setType(x, type); | |
363 break; | |
364 default: | |
365 throw new InternalCompilerException("Unexpected element " | |
366 + x.toString() + " kind: " + ElementKind.of(element) | |
367 + " evaluating type for compile-time constant expression."); | |
368 } | |
369 return null; | |
370 } | |
371 | |
372 | |
373 @Override | |
374 public Void visitIntegerLiteral(DartIntegerLiteral x) { | |
375 setType(x, intType); | |
376 return null; | |
377 } | |
378 | |
379 @Override | |
380 public Void visitInvocation(DartInvocation x) { | |
381 // No need to traverse, invocations are always disallowed. | |
382 expectedConstant(x); | |
383 return null; | |
384 } | |
385 | |
386 @Override | |
387 public Void visitMapLiteral(DartMapLiteral x) { | |
388 if (!x.isConst()) { | |
389 expectedConstant(x); | |
390 } else { | |
391 for (DartMapLiteralEntry entry : x.getEntries()) { | |
392 entry.accept(this); | |
393 } | |
394 } | |
395 return null; | |
396 } | |
397 | |
398 @Override | |
399 public Void visitMethodInvocation(DartMethodInvocation x) { | |
400 // No need to traverse, method invocations are always disallowed. | |
401 expectedConstant(x); | |
402 return null; | |
403 } | |
404 | |
405 @Override | |
406 public Void visitNewExpression(DartNewExpression x) { | |
407 if (!x.isConst()) { | |
408 expectedConstant(x); | |
409 } else { | |
410 for (DartExpression arg : x.getArgs()) { | |
411 arg.accept(this); | |
412 } | |
413 } | |
414 setType(x, x.getConstructor().getType()); | |
415 return null; | |
416 } | |
417 | |
418 @Override | |
419 public Void visitParenthesizedExpression(DartParenthesizedExpression x) { | |
420 x.visitChildren(this); | |
421 Type type = getMostSpecificType(x.getExpression()); | |
422 setType(x, type); | |
423 return null; | |
424 } | |
425 | |
426 @Override | |
427 public Void visitPropertyAccess(DartPropertyAccess x) { | |
428 x.visitChildren(this); | |
429 switch (ElementKind.of(x.getQualifier().getSymbol())) { | |
430 case CLASS: | |
431 case LIBRARY: | |
432 case NONE: | |
433 // OK. | |
434 break; | |
435 default: | |
436 expectedConstant(x); | |
437 return null; | |
438 } | |
439 Element element = x.getName().getSymbol(); | |
440 if (element != null && !element.getModifiers().isConstant()) { | |
441 expectedConstant(x); | |
442 } | |
443 Type type = getMostSpecificType(x.getName()); | |
444 setType(x, type); | |
445 return null; | |
446 } | |
447 | |
448 @Override | |
449 public Void visitRedirectConstructorInvocation(DartRedirectConstructorInvoca tion x) { | |
450 Element element = x.getSymbol(); | |
451 if (element != null) { | |
452 if (!element.getModifiers().isConstant()) { | |
453 expectedConstant(x); | |
454 } | |
455 } | |
456 x.visitChildren(this); | |
457 return null; | |
458 } | |
459 | |
460 @Override | |
461 public Void visitStringInterpolation(DartStringInterpolation x) { | |
462 expectedConstant(x); | |
463 return null; | |
464 } | |
465 | |
466 @Override | |
467 public Void visitStringLiteral(DartStringLiteral x) { | |
468 setType(x, stringType); | |
469 return null; | |
470 } | |
471 | |
472 @Override | |
473 public Void visitSuperExpression(DartSuperExpression x) { | |
474 // No need to traverse further - super() expressions are never constant | |
475 expectedConstant(x); | |
476 return null; | |
477 } | |
478 | |
479 @Override | |
480 public Void visitThisExpression(DartThisExpression x) { | |
481 // No need to traverse, this expressions are always disallowed. | |
482 expectedConstant(x); | |
483 return null; | |
484 } | |
485 | |
486 @Override | |
487 public Void visitUnaryExpression(DartUnaryExpression x) { | |
488 x.visitChildren(this); | |
489 | |
490 Type type = getMostSpecificType(x.getArg()); | |
491 switch (x.getOperator()) { | |
492 case NOT: | |
493 if (checkBoolean(x, type)) { | |
494 setType(x, boolType); | |
495 } | |
496 break; | |
497 case SUB: | |
498 if (checkNumber(x, type)) { | |
499 setType(x, numType); | |
500 } | |
501 break; | |
502 case BIT_NOT: | |
503 if (checkInt(x, type)) { | |
504 setType(x, intType); | |
505 } | |
506 break; | |
507 default: | |
508 expectedConstant(x); | |
509 } | |
510 return null; | |
511 } | |
512 | |
513 @Override | |
514 public Void visitUnqualifiedInvocation(DartUnqualifiedInvocation x) { | |
515 // No need to traverse, always disallowed. | |
516 expectedConstant(x); | |
517 return null; | |
518 } | |
519 } | |
520 | |
521 public void exec (DartUnit unit) { | |
522 unit.accept(this); | |
523 } | |
524 | |
525 public Set<Symbol> visitedSymbols = Sets.newHashSet(); | |
526 | |
527 public Map<DartNode, Type> inferredTypes = Maps.newHashMap(); | |
528 | |
529 private final DartCompilerContext context; | |
530 private final Type boolType; | |
531 private final Type doubleType; | |
532 private final Type intType; | |
533 private final Type numType; | |
534 private final Type stringType; | |
535 private final Type dynamicType; | |
536 | |
537 public CompileTimeConstantAnalyzer(CoreTypeProvider typeProvider, | |
538 DartCompilerContext context) { | |
539 this.context = context; | |
540 this.boolType = typeProvider.getBoolType(); | |
541 this.doubleType = typeProvider.getDoubleType(); | |
542 this.intType = typeProvider.getIntType(); | |
543 this.numType = typeProvider.getNumType(); | |
544 this.stringType = typeProvider.getStringType(); | |
545 this.dynamicType = typeProvider.getDynamicType(); | |
546 } | |
547 | |
548 private void checkConstantExpression(DartExpression expression) { | |
549 if (expression != null) { | |
550 expression.accept(new ExpressionVisitor()); | |
551 } | |
552 } | |
553 | |
554 @Override | |
555 public Void visitField(DartField node) { | |
556 checkConstantExpression(node.getValue()); | |
557 return null; | |
558 } | |
559 | |
560 @Override | |
561 public Void visitMethodDefinition(DartMethodDefinition node) { | |
562 DartFunction functionNode = node.getFunction(); | |
563 List<DartParameter> parameters = functionNode.getParams(); | |
564 for (DartParameter parameter : parameters) { | |
565 // Then resolve the default values. | |
566 checkConstantExpression(parameter.getDefaultExpr()); | |
567 } | |
568 return null; | |
569 } | |
570 | |
571 @Override | |
572 public Void visitNewExpression(DartNewExpression node) { | |
573 if (node.isConst()) { | |
574 for (DartExpression arg : node.getArgs()) { | |
575 checkConstantExpression(arg); | |
576 } | |
577 } | |
578 return null; | |
579 } | |
580 | |
581 @Override | |
582 public Void visitParameter(DartParameter node) { | |
583 checkConstantExpression(node.getDefaultExpr()); | |
584 return null; | |
585 } | |
586 | |
587 @Override | |
588 public Void visitVariableStatement(DartVariableStatement node) { | |
589 for (DartVariable variable : node.getVariables()) { | |
590 Modifiers modifiers = node.getModifiers(); | |
591 if (modifiers.isStatic() && modifiers.isFinal() && variable.getValue() != null) { | |
592 checkConstantExpression(variable.getValue()); | |
593 } | |
594 } | |
595 return null; | |
596 } | |
597 } | |
OLD | NEW |