nashorn/src/jdk/nashorn/internal/ir/Expression.java
changeset 24751 ccbd9cd3f720
parent 24719 f726e9d67629
child 24759 31aed7d9c02a
--- a/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Mon May 05 14:17:20 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Tue May 13 11:30:40 2014 +0200
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.internal.ir;
 
+import java.util.function.Function;
 import jdk.nashorn.internal.codegen.types.Type;
 
 /**
@@ -33,7 +34,12 @@
  *
  */
 public abstract class Expression extends Node {
-    private Symbol symbol;
+    private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
+        @Override
+        public Type apply(Symbol t) {
+            return null;
+        }
+    };
 
     Expression(final long token, final int start, final int finish) {
         super(token, start, finish);
@@ -45,57 +51,25 @@
 
     Expression(final Expression expr) {
         super(expr);
-        this.symbol = expr.symbol;
-    }
-
-    /**
-     * Return the Symbol the compiler has assigned to this Node. The symbol
-     * is the place where it's expression value is stored after evaluation
-     *
-     * @return the symbol
-     */
-    public Symbol getSymbol() {
-        return symbol;
     }
 
     /**
-     * Assign a symbol to this node. See {@link Expression#getSymbol()} for explanation
-     * of what a symbol is
+     * Returns the type of the expression.
      *
-     * @param lc lexical context
-     * @param symbol the symbol
-     * @return new node
+     * @return the type of the expression.
      */
-    public Expression setSymbol(final LexicalContext lc, final Symbol symbol) {
-        if (this.symbol == symbol) {
-            return this;
-        }
-        final Expression newExpr = (Expression)clone();
-        newExpr.symbol = symbol;
-        return newExpr;
+    public final Type getType() {
+        return getType(UNKNOWN_LOCALS);
     }
 
     /**
-     * Check if the expression has a type. The default behavior is to go into the symbol
-     * and check the symbol type, but there may be overrides, for example in
-     * getters that require a different type than the internal representation
-     *
-     * @return true if a type exists
+     * Returns the type of the expression under the specified symbol-to-type mapping. By default delegates to
+     * {@link #getType()} but expressions whose type depends on their subexpressions' types and expressions whose type
+     * depends on symbol type ({@link IdentNode}) will have a special implementation.
+     * @param localVariableTypes a mapping from symbols to their types, used for type calculation.
+     * @return the type of the expression under the specified symbol-to-type mapping.
      */
-    public boolean hasType() {
-        return getSymbol() != null;
-    }
-
-    /**
-     * Returns the type of the expression. Typically this is the symbol type. No types
-     * are stored in the expression itself, unless it implements TypeOverride.
-     *
-     * @return the type of the node.
-     */
-    public Type getType() {
-        assert hasType() : this + " has no type";
-        return symbol.getSymbolType();
-    }
+    public abstract Type getType(final Function<Symbol, Type> localVariableTypes);
 
     /**
      * Returns {@code true} if this expression depends exclusively on state that is constant
@@ -108,4 +82,88 @@
     public boolean isLocal() {
         return false;
     }
+
+    /**
+     * Is this a self modifying assignment?
+     * @return true if self modifying, e.g. a++, or a*= 17
+     */
+    public boolean isSelfModifying() {
+        return false;
+    }
+
+    /**
+     * Returns widest operation type of this operation.
+     *
+     * @return the widest type for this operation
+     */
+    public Type getWidestOperationType() {
+        return Type.OBJECT;
+    }
+
+    /**
+     * Returns true if the type of this expression is narrower than its widest operation type (thus, it is
+     * optimistically typed).
+     * @return true if this expression is optimistically typed.
+     */
+    public final boolean isOptimistic() {
+        return getType().narrowerThan(getWidestOperationType());
+    }
+
+    static final String OPT_IDENTIFIER = "%";
+
+    void optimisticTypeToString(final StringBuilder sb) {
+        optimisticTypeToString(sb, isOptimistic());
+    }
+
+    void optimisticTypeToString(final StringBuilder sb, boolean optimistic) {
+        sb.append('{');
+        final Type type = getType();
+        final String desc = type == Type.UNDEFINED ? "U" : type.getDescriptor();
+
+        sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
+        if(isOptimistic() && optimistic) {
+            sb.append(OPT_IDENTIFIER);
+            sb.append('_');
+            sb.append(((Optimistic)this).getProgramPoint());
+        }
+        sb.append('}');
+    }
+
+    /**
+     * Returns true if the runtime value of this expression is always false when converted to boolean as per ECMAScript
+     * ToBoolean conversion. Used in control flow calculations.
+     * @return true if this expression's runtime value converted to boolean is always false.
+     */
+    public boolean isAlwaysFalse() {
+        return false;
+    }
+
+    /**
+     * Returns true if the runtime value of this expression is always true when converted to boolean as per ECMAScript
+     * ToBoolean conversion. Used in control flow calculations.
+     * @return true if this expression's runtime value converted to boolean is always true.
+     */
+    public boolean isAlwaysTrue() {
+        return false;
+    }
+
+    /**
+     * Returns true if the expression is not null and {@link #isAlwaysFalse()}.
+     * @param test a test expression used as a predicate of a branch or a loop.
+     * @return true if the expression is not null and {@link #isAlwaysFalse()}.
+     */
+    public static boolean isAlwaysFalse(Expression test) {
+        return test != null && test.isAlwaysFalse();
+    }
+
+
+    /**
+     * Returns true if the expression is null or {@link #isAlwaysTrue()}. Null is considered to be always true as a
+     * for loop with no test is equivalent to a for loop with always-true test.
+     * @param test a test expression used as a predicate of a branch or a loop.
+     * @return true if the expression is null or {@link #isAlwaysFalse()}.
+     */
+    public static boolean isAlwaysTrue(Expression test) {
+        return test == null || test.isAlwaysTrue();
+    }
 }