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

Unified 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: Added new test to exercise code that was throwing ICE 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 side-by-side diff with in-line comments
Download patch
Index: compiler/java/com/google/dart/compiler/resolver/CompileTimeConstantAnalyzer.java
diff --git a/compiler/java/com/google/dart/compiler/resolver/CompileTimeConstantAnalyzer.java b/compiler/java/com/google/dart/compiler/resolver/CompileTimeConstantAnalyzer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b00c88ef7291a4d8aad98b946ce45d519bec292
--- /dev/null
+++ b/compiler/java/com/google/dart/compiler/resolver/CompileTimeConstantAnalyzer.java
@@ -0,0 +1,572 @@
+// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.google.dart.compiler.resolver;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.dart.compiler.DartCompilationError;
+import com.google.dart.compiler.DartCompilationPhase;
+import com.google.dart.compiler.DartCompilerContext;
+import com.google.dart.compiler.InternalCompilerException;
+import com.google.dart.compiler.ast.DartArrayLiteral;
+import com.google.dart.compiler.ast.DartBinaryExpression;
+import com.google.dart.compiler.ast.DartBooleanLiteral;
+import com.google.dart.compiler.ast.DartDoubleLiteral;
+import com.google.dart.compiler.ast.DartExpression;
+import com.google.dart.compiler.ast.DartField;
+import com.google.dart.compiler.ast.DartFunction;
+import com.google.dart.compiler.ast.DartFunctionObjectInvocation;
+import com.google.dart.compiler.ast.DartIdentifier;
+import com.google.dart.compiler.ast.DartIntegerLiteral;
+import com.google.dart.compiler.ast.DartInvocation;
+import com.google.dart.compiler.ast.DartMapLiteral;
+import com.google.dart.compiler.ast.DartMapLiteralEntry;
+import com.google.dart.compiler.ast.DartMethodDefinition;
+import com.google.dart.compiler.ast.DartMethodInvocation;
+import com.google.dart.compiler.ast.DartNewExpression;
+import com.google.dart.compiler.ast.DartNode;
+import com.google.dart.compiler.ast.DartNodeTraverser;
+import com.google.dart.compiler.ast.DartParameter;
+import com.google.dart.compiler.ast.DartParenthesizedExpression;
+import com.google.dart.compiler.ast.DartPropertyAccess;
+import com.google.dart.compiler.ast.DartRedirectConstructorInvocation;
+import com.google.dart.compiler.ast.DartStringInterpolation;
+import com.google.dart.compiler.ast.DartStringLiteral;
+import com.google.dart.compiler.ast.DartSuperExpression;
+import com.google.dart.compiler.ast.DartThisExpression;
+import com.google.dart.compiler.ast.DartUnaryExpression;
+import com.google.dart.compiler.ast.DartUnit;
+import com.google.dart.compiler.ast.DartUnqualifiedInvocation;
+import com.google.dart.compiler.ast.DartVariable;
+import com.google.dart.compiler.ast.DartVariableStatement;
+import com.google.dart.compiler.ast.Modifiers;
+import com.google.dart.compiler.common.Symbol;
+import com.google.dart.compiler.type.Type;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Given an tree, finds all compile-time constant expressions, and determines if
+ * each expression matches all the rules for a compile-time constant. Emits a
+ * resolution error if not.
+ *
+ * This script doesn't just resolve expressions, it also sets types to the
+ * extent needed to validate compile-time constant expressions (boolean, int,
+ * double, and string types might be set)
+ */
+public class CompileTimeConstantAnalyzer {
+
+ private class ExpressionVisitor extends DartNodeTraverser<Void> {
+ private ExpressionVisitor() {
+ }
+
+ private boolean checkBoolean(DartNode x, Type type) {
+ if (!type.equals(boolType)) {
+ context.onError(new DartCompilationError(x,
+ ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_BOOLEAN, type
+ .toString()));
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkInt(DartNode x, Type type) {
+ if (!type.equals(intType)) {
+ context
+ .onError(new DartCompilationError(x,
+ ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_INT, type
+ .toString()));
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkNumber(DartNode x, Type type) {
+ if (!(type.equals(numType) || type.equals(intType) || type
+ .equals(doubleType))) {
+ context.onError(new DartCompilationError(x,
+ ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_NUMBER, type
+ .toString()));
+
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkNumberBooleanOrStringType(DartNode x, Type type) {
+ if (!type.equals(intType) && !type.equals(boolType)
+ && !type.equals(numType) && !type.equals(doubleType)
+ && !type.equals(stringType)) {
+ context.onError(new DartCompilationError(x,
+ ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION_STRING_NUMBER_BOOL,
+ type.toString()));
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Logs a general message "expected a constant expression" error. Use a more
+ * specific error message when possible.
+ */
+ private void expectedConstant(DartNode x) {
+ context.onError(new DartCompilationError(x,
+ ResolverErrorCode.EXPECTED_CONSTANT_EXPRESSION));
+ }
+
+ /**
+ * Determine the most specific type assigned to an expression node. Prefer
+ * the setting in the expression's symbol if present. Otherwise, use a type
+ * tagged in the expression node itself.
+ *
+ * @return a non <code>null</code> type value. Dynamic if none other can be
+ * determined.
+ */
+ private Type getMostSpecificType(DartNode node) {
+ Element element = (Element) node.getSymbol();
+ Type type = inferredTypes.get(node);
+ if (type != null) {
+ return type;
+ }
+ if (element != null) {
+ type = element.getType();
+ if (type != null) {
+ return type;
+ }
+ }
+ return dynamicType;
+ }
+
+ private void rememberInferredType(DartNode x, Type type) {
+ if (type != null && ! type.equals(dynamicType)) {
+ inferredTypes.put(x, type);
+ }
+ }
+
+ @Override
+ public Void visitArrayLiteral(DartArrayLiteral x) {
+ if (!x.isConst()) {
+ expectedConstant(x);
+ } else {
+ for (DartExpression expr : x.getExpressions()) {
+ expr.accept(this);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryExpression(DartBinaryExpression x) {
+ x.visitChildren(this);
+
+ DartExpression lhs = x.getArg1();
+ DartExpression rhs = x.getArg2();
+ Type lhsType = getMostSpecificType(lhs);
+ Type rhsType = getMostSpecificType(rhs);
+ if (lhsType == null) {
+ lhsType = dynamicType;
+ }
+ if (rhsType == null) {
+ rhsType = dynamicType;
+ }
+
+ switch (x.getOperator()) {
+ case NE:
+ case EQ:
+ case NE_STRICT:
+ case EQ_STRICT:
+ if (checkNumberBooleanOrStringType(lhs, lhsType)
+ && checkNumberBooleanOrStringType(rhs, rhsType)) {
+ rememberInferredType(x, boolType);
+ }
+ break;
+
+ case AND:
+ case OR:
+ if (checkBoolean(lhs, lhsType) && checkBoolean(rhs, rhsType)) {
+ rememberInferredType(x, boolType);
+ }
+ break;
+
+ case BIT_NOT:
+ case TRUNC:
+ case BIT_XOR:
+ case BIT_AND:
+ case BIT_OR:
+ case SAR:
+ case SHL:
+ if (checkInt(lhs, lhsType) && checkInt(rhs, rhsType)) {
+ rememberInferredType(x, intType);
+ }
+ break;
+
+ case ADD:
+ case SUB:
+ case MUL:
+ case DIV:
+ case MOD:
+ if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) {
+ rememberInferredType(x, numType);
+ }
+ break;
+ case LT:
+ case GT:
+ case LTE:
+ case GTE:
+ if (checkNumber(lhs, lhsType) && checkNumber(rhs, rhsType)) {
+ rememberInferredType(x, boolType);
+ }
+ break;
+
+ default:
+ // all other operators...
+ expectedConstant(x);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBooleanLiteral(DartBooleanLiteral x) {
+ rememberInferredType(x, boolType);
+ return null;
+ }
+
+ @Override
+ public Void visitDoubleLiteral(DartDoubleLiteral x) {
+ rememberInferredType(x, doubleType);
+ return null;
+ }
+
+ @Override
+ public Void visitField(DartField x) {
+ x.visitChildren(this);
+ if (x.getType() == null || x.getType().equals(dynamicType)) {
+ Type type = getMostSpecificType(x.getValue());
+ rememberInferredType(x, type);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitFunction(DartFunction x) {
+ // No need to traverse, functions are always disallowed.
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitFunctionObjectInvocation(DartFunctionObjectInvocation x) {
+ // No need to traverse, function object invocations are always disallowed.
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitIdentifier(DartIdentifier x) {
+ x.visitChildren(this);
+
+ Element element = x.getSymbol();
+ switch (ElementKind.of(element)) {
+ case CLASS:
+ case PARAMETER:
+ case LIBRARY:
+ break;
+
+ case FIELD:
+ case VARIABLE:
+
+ if (element != null && visitedSymbols.contains(element)) {
+ context.onError(new DartCompilationError(x,
+ ResolverErrorCode.CIRCULAR_REFERENCE));
+ rememberInferredType(x, getMostSpecificType(x));
+ return null;
+ }
+ visitedSymbols.add(element);
+ if (!element.getModifiers().isConstant()) {
+ expectedConstant(x);
+ }
+ DartNode identifierNode = element.getNode();
+ this.visit(Lists.newArrayList(identifierNode));
+ visitedSymbols.remove(element);
+
+ switch (ElementKind.of(element)) {
+ case FIELD:
+ rememberInferredType(x, getMostSpecificType(identifierNode));
+ break;
+ }
+ break;
+
+ case CONSTRUCTOR:
+ if (!element.getModifiers().isConstant()) {
+ expectedConstant(x);
+ }
+ rememberInferredType(x, getMostSpecificType(x));
+ break;
+
+ case NONE:
+ Type type = getMostSpecificType(x);
+ if (dynamicType.equals(type)) {
+ // This is the case for unresolved identifiers
+ expectedConstant(x);
+ }
+ rememberInferredType(x, type);
+ break;
+
+ default:
+ throw new InternalCompilerException("Unexpected element "
+ + x.toString() + " kind: " + ElementKind.of(element)
+ + " evaluating type for compile-time constant expression.");
+ }
+ return null;
+ }
+
+
+ @Override
+ public Void visitIntegerLiteral(DartIntegerLiteral x) {
+ rememberInferredType(x, intType);
+ return null;
+ }
+
+ @Override
+ public Void visitInvocation(DartInvocation x) {
+ // No need to traverse, invocations are always disallowed.
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitMapLiteral(DartMapLiteral x) {
+ if (!x.isConst()) {
+ expectedConstant(x);
+ } else {
+ for (DartMapLiteralEntry entry : x.getEntries()) {
+ entry.accept(this);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitMethodInvocation(DartMethodInvocation x) {
+ // No need to traverse, method invocations are always disallowed.
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitNewExpression(DartNewExpression x) {
+ if (!x.isConst()) {
+ expectedConstant(x);
+ } else {
+ for (DartExpression arg : x.getArgs()) {
+ arg.accept(this);
+ }
+ }
+ rememberInferredType(x, x.getConstructor().getType());
+ return null;
+ }
+
+ @Override
+ public Void visitParenthesizedExpression(DartParenthesizedExpression x) {
+ x.visitChildren(this);
+ Type type = getMostSpecificType(x.getExpression());
+ rememberInferredType(x, type);
+ return null;
+ }
+
+ @Override
+ public Void visitPropertyAccess(DartPropertyAccess x) {
+ x.visitChildren(this);
+ switch (ElementKind.of(x.getQualifier().getSymbol())) {
+ case CLASS:
+ case LIBRARY:
+ case NONE:
+ // OK.
+ break;
+ default:
+ expectedConstant(x);
+ return null;
+ }
+
+ Element element = x.getName().getSymbol();
+ if (element != null && !element.getModifiers().isConstant()) {
+ expectedConstant(x);
+ }
+ Type type = getMostSpecificType(x.getName());
+ rememberInferredType(x, type);
+ return null;
+ }
+
+ @Override
+ public Void visitRedirectConstructorInvocation(DartRedirectConstructorInvocation x) {
+ Element element = x.getSymbol();
+ if (element != null) {
+ if (!element.getModifiers().isConstant()) {
+ expectedConstant(x);
+ }
+ }
+ x.visitChildren(this);
+ return null;
+ }
+
+ @Override
+ public Void visitStringInterpolation(DartStringInterpolation x) {
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitStringLiteral(DartStringLiteral x) {
+ rememberInferredType(x, stringType);
+ return null;
+ }
+
+ @Override
+ public Void visitSuperExpression(DartSuperExpression x) {
+ // No need to traverse further - super() expressions are never constant
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitThisExpression(DartThisExpression x) {
+ // No need to traverse, this expressions are never constant
+ expectedConstant(x);
+ return null;
+ }
+
+ @Override
+ public Void visitUnaryExpression(DartUnaryExpression x) {
+ x.visitChildren(this);
+
+ Type type = getMostSpecificType(x.getArg());
+ switch (x.getOperator()) {
+ case NOT:
+ if (checkBoolean(x, type)) {
+ rememberInferredType(x, boolType);
+ }
+ break;
+ case SUB:
+ if (checkNumber(x, type)) {
+ rememberInferredType(x, numType);
+ }
+ break;
+ case BIT_NOT:
+ if (checkInt(x, type)) {
+ rememberInferredType(x, intType);
+ }
+ break;
+ default:
+ expectedConstant(x);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitUnqualifiedInvocation(DartUnqualifiedInvocation x) {
+ // No need to traverse, always disallowed.
+ expectedConstant(x);
+ return null;
+ }
+ }
+
+ private class FindCompileTimeConstantExpressionsVisitor extends DartNodeTraverser<Void> {
+
+ @Override
+ public Void visitField(DartField node) {
+ checkConstantExpression(node.getValue());
+ return null;
+ }
+
+ @Override
+ public Void visitMethodDefinition(DartMethodDefinition node) {
+ DartFunction functionNode = node.getFunction();
+ List<DartParameter> parameters = functionNode.getParams();
+ for (DartParameter parameter : parameters) {
+ // Then resolve the default values.
+ checkConstantExpression(parameter.getDefaultExpr());
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitNewExpression(DartNewExpression node) {
+ if (node.isConst()) {
+ for (DartExpression arg : node.getArgs()) {
+ checkConstantExpression(arg);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitParameter(DartParameter node) {
+ checkConstantExpression(node.getDefaultExpr());
+ return null;
+ }
+
+ @Override
+ public Void visitVariableStatement(DartVariableStatement node) {
+ for (DartVariable variable : node.getVariables()) {
+ Modifiers modifiers = node.getModifiers();
+ if (modifiers.isStatic() && modifiers.isFinal() && variable.getValue() != null) {
+ checkConstantExpression(variable.getValue());
+ }
+ }
+ return null;
+ }
+ }
+
+ public static class Phase implements DartCompilationPhase {
+ /**
+ * Executes symbol resolution on the given compilation unit.
+ *
+ * @param context The listener through which compilation errors are reported
+ * (not <code>null</code>)
+ */
+ @Override
+ public DartUnit exec(DartUnit unit, DartCompilerContext context,
+ CoreTypeProvider typeProvider) {
+ new CompileTimeConstantAnalyzer(typeProvider, context).exec(unit);
+ return unit;
+ }
+ }
+
+ public Set<Symbol> visitedSymbols = Sets.newHashSet();
+
+ public Map<DartNode, Type> inferredTypes = Maps.newHashMap();
+
+ private final DartCompilerContext context;
+ private final Type boolType;
+ private final Type doubleType;
+ private final Type intType;
+ private final Type numType;
+ private final Type stringType;
+ private final Type dynamicType;
+
+ public CompileTimeConstantAnalyzer(CoreTypeProvider typeProvider,
+ DartCompilerContext context) {
+ this.context = context;
+ this.boolType = typeProvider.getBoolType();
+ this.doubleType = typeProvider.getDoubleType();
+ this.intType = typeProvider.getIntType();
+ this.numType = typeProvider.getNumType();
+ this.stringType = typeProvider.getStringType();
+ this.dynamicType = typeProvider.getDynamicType();
+ }
+
+ private void checkConstantExpression(DartExpression expression) {
+ if (expression != null) {
+ expression.accept(new ExpressionVisitor());
+ }
+ }
+
+ public void exec (DartUnit unit) {
+ unit.accept(new FindCompileTimeConstantExpressionsVisitor());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698