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

Side by Side Diff: compiler/java/com/google/dart/compiler/resolver/CompileTimeConstantAnalyzer.java

Issue 8523034: Compile Time Constants cycle check (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Removed some unecessary files, updated language.status Created 9 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
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698