From 9b3b5570519634919030492e4dd9562cdd64d9a4 Mon Sep 17 00:00:00 2001 From: chiba Date: Wed, 1 Sep 2004 07:45:32 +0000 Subject: [PATCH] modified the code generator to compute constant expressions at compile time. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@130 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/CtField.java | 13 +- src/main/javassist/compiler/CodeGen.java | 4 +- .../javassist/compiler/MemberCodeGen.java | 8 +- .../javassist/compiler/MemberResolver.java | 2 +- src/main/javassist/compiler/TypeChecker.java | 200 +++++++++++++++--- src/main/javassist/compiler/ast/Expr.java | 2 + src/main/javassist/compiler/ast/Member.java | 10 + 7 files changed, 208 insertions(+), 31 deletions(-) diff --git a/src/main/javassist/CtField.java b/src/main/javassist/CtField.java index bbf76338..c537ed58 100644 --- a/src/main/javassist/CtField.java +++ b/src/main/javassist/CtField.java @@ -251,13 +251,17 @@ public class CtField extends CtMember { * A constant field is static and final. * * @return a Integer, Long, Float, - * Double, or String object + * Double, Boolean, + * or String object * representing the constant value. * null if it is not a constant field * or if the field type is not a primitive type * or String. */ 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 : diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 35d06817..bf6c97ef 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -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); } diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index a4e69f59..14e14438 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -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()); + } } /** diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java index 3c06f62c..08d9dd37 100644 --- a/src/main/javassist/compiler/MemberResolver.java +++ b/src/main/javassist/compiler/MemberResolver.java @@ -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 */ diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index 196c7642..9b505afb 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/src/main/javassist/compiler/TypeChecker.java @@ -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; } } diff --git a/src/main/javassist/compiler/ast/Expr.java b/src/main/javassist/compiler/ast/Expr.java index aafe8a75..81ae5821 100644 --- a/src/main/javassist/compiler/ast/Expr.java +++ b/src/main/javassist/compiler/ast/Expr.java @@ -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) { diff --git a/src/main/javassist/compiler/ast/Member.java b/src/main/javassist/compiler/ast/Member.java index ee54ecc4..f0303cd3 100644 --- a/src/main/javassist/compiler/ast/Member.java +++ b/src/main/javassist/compiler/ast/Member.java @@ -16,14 +16,24 @@ 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); } } -- 2.39.5