]> source.dussan.org Git - javassist.git/commitdiff
modified the code generator to compute constant expressions at compile time.
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 1 Sep 2004 07:45:32 +0000 (07:45 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Wed, 1 Sep 2004 07:45:32 +0000 (07:45 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@130 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/CtField.java
src/main/javassist/compiler/CodeGen.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/compiler/MemberResolver.java
src/main/javassist/compiler/TypeChecker.java
src/main/javassist/compiler/ast/Expr.java
src/main/javassist/compiler/ast/Member.java

index bbf76338a9b6bf20b682fd9548573cdf67820c6f..c537ed5897f409357e32c122f1f7a339381ea0a2 100644 (file)
@@ -251,13 +251,17 @@ public class CtField extends CtMember {
      * A constant field is <code>static</code> and <code>final</code>.
      *
      * @return  a <code>Integer</code>, <code>Long</code>, <code>Float</code>,
-     *          <code>Double</code>, or <code>String</code> object
+     *          <code>Double</code>, <code>Boolean</code>,
+     *          or <code>String</code> object
      *          representing the constant value. 
      *          <code>null</code> if it is not a constant field
      *          or if the field type is not a primitive type
      *          or <code>String</code>.
      */
     public Object getConstantValue() {
+        // When this method is modified,
+        // see also getConstantFieldValue() in TypeChecker.
+
         int index = fieldInfo.getConstantValue();
         if (index == 0)
             return null;
@@ -271,7 +275,12 @@ public class CtField extends CtMember {
             case ConstPool.CONST_Double :
                 return new Double(cp.getDoubleInfo(index));
             case ConstPool.CONST_Integer :
-                return new Integer(cp.getIntegerInfo(index));
+                int value = cp.getIntegerInfo(index);
+                // "Z" means boolean type.
+                if ("Z".equals(fieldInfo.getDescriptor()))
+                    return new Boolean(value != 0);
+                else
+                    return new Integer(value);
             case ConstPool.CONST_String :
                 return cp.getStringInfo(index);
             default :
index 35d068177d334c688373302cdcd41980cd3ac03c..bf6c97efa0779a8dc9536d3fbf76aede73472822 100644 (file)
@@ -843,7 +843,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
         if (p >= 0) {
             int op = binOp[index + p + 1];
             if (op != NOP) {
-                if (p == P_INT)
+                if (p == P_INT && exprType != BOOLEAN)
                     exprType = INT;     // type1 may be BYTE, ...
 
                 bytecode.addOpcode(op);
@@ -1281,7 +1281,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId {
         else if (token == MEMBER) {     // field read
             /* MEMBER ('#') is an extension by Javassist.
              * The compiler internally uses # for compiling .class
-             * expressions such as "int.class". 
+             * expressions such as "int.class".
              */
             atFieldRead(expr);
         }
index a4e69f59c4c294e979de436967a15b0e510c6c91..14e144389a321bffe45024f7886e88f73d898e75 100644 (file)
@@ -627,7 +627,13 @@ public class MemberCodeGen extends CodeGen {
     {
         CtField f = fieldAccess(expr);
         boolean is_static = resultStatic;
-        atFieldRead(f, is_static);
+        ASTree cexpr = TypeChecker.getConstantFieldValue(f);
+        if (cexpr == null)
+            atFieldRead(f, is_static);
+        else {
+            cexpr.accept(this);
+            setFieldType(f.getFieldInfo2());
+        }
     }
 
     /**
index 3c06f62cfe3ffd63a0b0ed23563d4cfd40209189..08d9dd37c04d7df7e4e0cc9e014c99d068013ef3 100644 (file)
@@ -228,7 +228,7 @@ public class MemberResolver implements TokenId {
     }
 
     /**
-     * Only used by fieldAccess() in MemberCodeGen.
+     * Only used by fieldAccess() in MemberCodeGen and TypeChecker.
      *
      * @param jvmClassName  a JVM class name.  e.g. java/lang/String
      */
index 196c7642e6e802cf943feebb1e38899cd350b092..9b505afb68135b65d15e8e42e888d389e2147f6e 100644 (file)
@@ -18,6 +18,7 @@ package javassist.compiler;
 import javassist.CtClass;
 import javassist.CtField;
 import javassist.ClassPool;
+import javassist.Modifier;
 import javassist.NotFoundException;
 import javassist.compiler.ast.*;
 import javassist.bytecode.*;
@@ -227,6 +228,12 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
             }
     }
 
+    /*
+     * If atBinExpr() substitutes a new expression for the original
+     * binary-operator expression, it changes the operator name to '+'
+     * (if the original is not '+') and sets the new expression to the
+     * left-hand-side expression and null to the right-hand-side expression. 
+     */
     public void atBinExpr(BinExpr expr) throws CompileError {
         int token = expr.getOperator();
         int k = CodeGen.lookupBinOp(token);
@@ -241,16 +248,19 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
                      */
                     e = CallExpr.makeCall(Expr.make('.', e,
                                             new Member("toString")), null);
-                    expr.setLeft(e);
+                    expr.setOprand1(e);
                     expr.setOprand2(null);    // <---- look at this!
                     className = jvmJavaLangString;
                 }
             }
             else {
-                expr.oprand1().accept(this);
+                ASTree left = expr.oprand1();
+                ASTree right = expr.oprand2();
+                left.accept(this);
                 int type1 = exprType;
-                expr.oprand2().accept(this);
-                computeBinExprType(expr, token, type1);
+                right.accept(this);
+                if (!isConstant(expr, token, left, right))
+                    computeBinExprType(expr, token, type1);
             }
         }
         else {
@@ -260,18 +270,17 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         }
     }
 
-    // expr must be a + expression.
+    /* EXPR must be a + expression.
+     * atPlusExpr() returns non-null if the given expression is string
+     * concatenation.  The returned value is "new StringBuffer().append..".
+     */
     private Expr atPlusExpr(BinExpr expr) throws CompileError {
         ASTree left = expr.oprand1();
         ASTree right = expr.oprand2();
         if (right == null) {
-            /* this expression has been already type-checked since it is
-               string concatenation.
-               see atBinExpr() above.
-             */
-            exprType = CLASS;
-            arrayDim = 0;
-            className = jvmJavaLangString;
+            // this expression has been already type-checked.
+            // see atBinExpr() above.
+            left.accept(this);
             return null;
         }
 
@@ -292,6 +301,10 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         int dim1 = arrayDim;
         String cname = className;
         right.accept(this);
+
+        if (isConstant(expr, '+', left, right))
+            return null;
+
         if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname))
             || (exprType == CLASS && arrayDim == 0
                 && jvmJavaLangString.equals(className))) {
@@ -309,6 +322,91 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         }
     }
 
+    private boolean isConstant(BinExpr expr, int op, ASTree left,
+                               ASTree right) throws CompileError
+    {
+        left = stripPlusExpr(left);
+        right = stripPlusExpr(right);
+        ASTree newExpr = null;
+        if (left instanceof StringL && right instanceof StringL && op == '+')
+            newExpr = new StringL(((StringL)left).get()
+                                  + ((StringL)right).get());
+        else if (left instanceof IntConst)
+            newExpr = ((IntConst)left).compute(op, right);
+        else if (left instanceof DoubleConst)
+            newExpr = ((DoubleConst)left).compute(op, right);
+
+        if (newExpr == null)
+            return false;       // not a constant expression
+        else {
+            expr.setOperator('+');
+            expr.setOprand1(newExpr);
+            expr.setOprand2(null);
+            newExpr.accept(this);   // for setting exprType, arrayDim, ...
+            return true;
+        }
+    }
+
+    private static ASTree stripPlusExpr(ASTree expr) {
+        if (expr instanceof BinExpr) {
+            BinExpr e = (BinExpr)expr;
+            if (e.getOperator() == '+' && e.oprand2() == null)
+                return e.getLeft();
+        }
+        else if (expr instanceof Expr) {    // note: BinExpr extends Expr.
+            Expr e = (Expr)expr;
+            int op = e.getOperator();
+            if (op == MEMBER) {
+                ASTree cexpr = getConstantFieldValue((Member)e.oprand2());
+                if (cexpr != null)
+                    return cexpr;
+            }
+            else if (op == '+' && e.getRight() == null)
+                return e.getLeft();
+        }
+        else if (expr instanceof Member) {
+            ASTree cexpr = getConstantFieldValue((Member)expr);
+            if (cexpr != null)
+                return cexpr;
+        }
+
+        return expr;
+    }
+
+    /**
+     * If MEM is a static final field, this method returns a constant
+     * expression representing the value of that field.
+     */
+    private static ASTree getConstantFieldValue(Member mem) {
+        return getConstantFieldValue(mem.getField());
+    }
+
+    public static ASTree getConstantFieldValue(CtField f) {
+        if (f == null)
+            return null;
+
+        Object value = f.getConstantValue();
+        if (value == null)
+            return null;
+
+        if (value instanceof String)
+            return new StringL((String)value);
+        else if (value instanceof Double || value instanceof Float) {
+            int token = (value instanceof Double)
+                        ? DoubleConstant : FloatConstant;
+            return new DoubleConst(((Number)value).doubleValue(), token);
+        }
+        else if (value instanceof Number) {
+            int token = (value instanceof Long) ? LongConstant : IntConstant; 
+            return new IntConst(((Number)value).longValue(), token);
+        }
+        else if (value instanceof Boolean)
+            return new Keyword(((Boolean)value).booleanValue()
+                               ? TokenId.TRUE : TokenId.FALSE);
+        else
+            return null;
+    }
+
     private static boolean isPlusExpr(ASTree expr) {
         if (expr instanceof BinExpr) {
             BinExpr bexpr = (BinExpr)expr;
@@ -419,13 +517,42 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         else if (token == CALL)              // method call
             fatal();
         else {
-            expr.oprand1().accept(this);
-            if (token == '-' || token == '~')
-                if (CodeGen.isP_INT(exprType))
-                    exprType = INT;         // type may be BYTE, ...
+            oprand.accept(this);
+            if (!isConstant(expr, token, oprand))
+                if (token == '-' || token == '~')
+                    if (CodeGen.isP_INT(exprType))
+                        exprType = INT;         // type may be BYTE, ...
         }
     }
 
+    private boolean isConstant(Expr expr, int op, ASTree oprand) {
+        oprand = stripPlusExpr(oprand);
+        if (oprand instanceof IntConst) {
+            IntConst c = (IntConst)oprand;
+            long v = c.get();
+            if (op == '-')
+                v = -v;
+            else if (op == '~')
+                v = ~v;
+            else
+                return false;
+
+            c.set(v);
+        }
+        else if (oprand instanceof DoubleConst) {
+            DoubleConst c = (DoubleConst)oprand;
+            if (op == '-')
+                c.set(-c.get());
+            else
+                return false;
+        }
+        else
+            return false;
+
+        expr.setOperator('+');
+        return true;
+    }
+
     public void atCallExpr(CallExpr expr) throws CompileError {
         String mname = null;
         CtClass targetClass = null;
@@ -464,6 +591,9 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
                     exprType = CLASS;
                     arrayDim = 0;
                     className = nfe.getField(); // JVM-internal
+                    e.setOperator(MEMBER);
+                    e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
+                                                            className)));
                 }
 
                 if (arrayDim > 0)
@@ -480,7 +610,7 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
             fatal();
 
         MemberResolver.Method minfo
-            = atMethodCallCore(targetClass, mname, args);
+                = atMethodCallCore(targetClass, mname, args);
         expr.setMethod(minfo);
     }
 
@@ -591,11 +721,21 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
             className = null;
     }
 
+    /* if EXPR is to access a static field, fieldAccess() translates EXPR
+     * into an expression using '#' (MEMBER).  For example, it translates
+     * java.lang.Integer.TYPE into java.lang.Integer#TYPE.  This translation
+     * speeds up type resolution by MemberCodeGen.
+     */
     protected CtField fieldAccess(ASTree expr) throws CompileError {
         if (expr instanceof Member) {
-            String name = ((Member)expr).get();
+            Member mem = (Member)expr;
+            String name = mem.get();
             try {
-                return thisClass.getField(name);
+                CtField f = thisClass.getField(name);
+                if (Modifier.isStatic(f.getModifiers()))
+                    mem.setField(f);
+
+                return f;
             }
             catch (NotFoundException e) {
                 // EXPR might be part of a static member access?
@@ -605,9 +745,13 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
         else if (expr instanceof Expr) {
             Expr e = (Expr)expr;
             int op = e.getOperator();
-            if (op == MEMBER)
-                return resolver.lookupField(((Symbol)e.oprand1()).get(),
-                                            (Symbol)e.oprand2());
+            if (op == MEMBER) {
+                Member mem = (Member)e.oprand2();
+                CtField f
+                    = resolver.lookupField(((Symbol)e.oprand1()).get(), mem);
+                mem.setField(f);
+                return f;
+            }
             else if (op == '.')
                 try {
                     e.oprand1().accept(this);
@@ -623,9 +767,15 @@ public class TypeChecker extends Visitor implements Opcode, TokenId {
                      * If EXPR might be part of a qualified class name,
                      * lookupFieldByJvmName2() throws NoFieldException.
                      */
-                    Symbol fname = (Symbol)e.oprand2();
-                    return resolver.lookupFieldByJvmName2(nfe.getField(),
-                                                          fname, expr);
+                    Member fname = (Member)e.oprand2();
+                    String jvmClassName = nfe.getField();
+                    CtField f = resolver.lookupFieldByJvmName2(jvmClassName,
+                                                               fname, expr);
+                    e.setOperator(MEMBER);
+                    e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
+                                                            jvmClassName)));
+                    fname.setField(f);
+                    return f;
                 }
         }
 
index aafe8a75d273f3ad87efd6923986a0be965fee2d..81ae582182ef39a0bf0825bb039c988a35b03ade 100644 (file)
@@ -50,6 +50,8 @@ public class Expr extends ASTList implements TokenId {
 
     public int getOperator() { return operatorId; }
 
+    public void setOperator(int op) { operatorId = op; }
+
     public ASTree oprand1() { return getLeft(); }
 
     public void setOprand1(ASTree expr) {
index ee54ecc406577328cb7931f79c263699544f0cba..f0303cd313c67987dc596192e8f492072c2dbeb9 100644 (file)
 package javassist.compiler.ast;
 
 import javassist.compiler.CompileError;
+import javassist.CtField;
 
 /**
  * Member name.
  */
 public class Member extends Symbol {
+    // cache maintained by fieldAccess() in TypeChecker.
+    // this is used to obtain the value of a static final field.
+    private CtField field;
+
     public Member(String name) {
         super(name);
+        field = null;
     }
 
+    public void setField(CtField f) { field = f; }
+
+    public CtField getField() { return field; }
+
     public void accept(Visitor v) throws CompileError { v.atMember(this); }
 }