git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@55 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -61,6 +61,8 @@ public class URLClassPath implements ClassPath { | |||
/** | |||
* Opens a class file with http. | |||
* | |||
* @return null if the class file is not found. | |||
*/ | |||
public InputStream openClassfile(String classname) { | |||
try { |
@@ -33,6 +33,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
protected Bytecode bytecode; | |||
private int tempVar; | |||
TypeChecker typeChecker; | |||
/** | |||
* true if the last visited node is a return statement. | |||
@@ -56,12 +57,17 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
public CodeGen(Bytecode b) { | |||
bytecode = b; | |||
tempVar = -1; | |||
typeChecker = null; | |||
hasReturned = false; | |||
inStaticMethod = false; | |||
breakList = null; | |||
continueList = null; | |||
} | |||
public void setTypeChecker(TypeChecker checker) { | |||
typeChecker = checker; | |||
} | |||
protected static void fatal() throws CompileError { | |||
throw new CompileError("fatal"); | |||
} | |||
@@ -192,29 +198,21 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
return sbuf.toString(); | |||
} | |||
protected static int jvmTypeNameToExprType(char type) { | |||
switch(type) { | |||
case 'Z' : | |||
return BOOLEAN; | |||
case 'B' : | |||
return BYTE; | |||
case 'C' : | |||
return CHAR; | |||
case 'S' : | |||
return SHORT; | |||
case 'I' : | |||
return INT; | |||
case 'J' : | |||
return LONG; | |||
case 'F' : | |||
return FLOAT; | |||
case 'D' : | |||
return DOUBLE; | |||
case 'V' : | |||
return VOID; | |||
default : | |||
return CLASS; | |||
} | |||
public void compileExpr(ASTree expr) throws CompileError { | |||
doTypeCheck(expr); | |||
expr.accept(this); | |||
} | |||
public boolean compileBooleanExpr(boolean branchIf, ASTree expr) | |||
throws CompileError | |||
{ | |||
doTypeCheck(expr); | |||
return booleanExpr(branchIf, expr); | |||
} | |||
public void doTypeCheck(ASTree expr) throws CompileError { | |||
if (typeChecker != null) | |||
expr.accept(typeChecker); | |||
} | |||
public void atASTList(ASTList n) throws CompileError { fatal(); } | |||
@@ -302,6 +300,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int op = st.getOperator(); | |||
if (op == EXPR) { | |||
ASTree expr = st.getLeft(); | |||
doTypeCheck(expr); | |||
if (expr instanceof AssignExpr) | |||
atAssignExpr((AssignExpr)expr, false); | |||
else if (isPlusPlusExpr(expr)) { | |||
@@ -359,7 +358,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
ASTree expr = st.head(); | |||
Stmnt thenp = (Stmnt)st.tail().head(); | |||
Stmnt elsep = (Stmnt)st.tail().tail().head(); | |||
booleanExpr(false, expr); | |||
compileBooleanExpr(false, expr); | |||
int pc = bytecode.currentPc(); | |||
int pc2 = 0; | |||
bytecode.addIndex(0); // correct later | |||
@@ -412,7 +411,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
if (notDo) | |||
bytecode.write16bit(pc, pc3 - pc + 1); | |||
boolean alwaysBranch = booleanExpr(true, expr); | |||
boolean alwaysBranch = compileBooleanExpr(true, expr); | |||
bytecode.addIndex(pc2 - bytecode.currentPc() + 1); | |||
patchGoto(breakList, bytecode.currentPc()); | |||
@@ -449,7 +448,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int pc = bytecode.currentPc(); | |||
int pc2 = 0; | |||
if (expr != null) { | |||
booleanExpr(false, expr); | |||
compileBooleanExpr(false, expr); | |||
pc2 = bytecode.currentPc(); | |||
bytecode.addIndex(0); | |||
} | |||
@@ -500,7 +499,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
if (result == null) | |||
op = Opcode.RETURN; | |||
else { | |||
result.accept(this); | |||
compileExpr(result); | |||
if (arrayDim > 0) | |||
op = ARETURN; | |||
else { | |||
@@ -524,7 +523,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
private void atThrowStmnt(Stmnt st) throws CompileError { | |||
ASTree e = st.getLeft(); | |||
e.accept(this); | |||
compileExpr(e); | |||
if (exprType != CLASS || arrayDim > 0) | |||
throw new CompileError("bad throw statement"); | |||
@@ -560,8 +559,10 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
/* NOTE: Array initializers has not been supported. | |||
*/ | |||
ASTree init = d.getInitializer(); | |||
if (init != null) | |||
if (init != null) { | |||
doTypeCheck(init); | |||
atVariableAssign(null, '=', null, d, init, false); | |||
} | |||
} | |||
public abstract void atNewExpr(NewExpr n) throws CompileError; | |||
@@ -681,20 +682,22 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int type, int dim, String cname) | |||
throws CompileError | |||
{ | |||
right.accept(this); | |||
if (invalidDim(exprType, arrayDim, className, type, dim, cname, false) | |||
|| (op != '=' && dim > 0)) | |||
badAssign(expr); | |||
if (op == PLUS_E && dim == 0 && type == CLASS) | |||
atStringConcatExpr(expr, type, dim, cname); | |||
else if (op != '=') { | |||
int token = assignOps[op - MOD_E]; | |||
int k = lookupBinOp(token); | |||
if (k < 0) | |||
fatal(); | |||
atStringPlusEq(expr, type, dim, cname, right); | |||
else { | |||
right.accept(this); | |||
if (invalidDim(exprType, arrayDim, className, type, dim, cname, | |||
false) || (op != '=' && dim > 0)) | |||
badAssign(expr); | |||
if (op != '=') { | |||
int token = assignOps[op - MOD_E]; | |||
int k = lookupBinOp(token); | |||
if (k < 0) | |||
fatal(); | |||
atArithBinExpr(expr, token, k, type); | |||
atArithBinExpr(expr, token, k, type); | |||
} | |||
} | |||
if (op != '=' || (dim == 0 && !isRefType(type))) | |||
@@ -703,6 +706,23 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
// type check should be done here. | |||
} | |||
private void atStringPlusEq(Expr expr, int type, int dim, String cname, | |||
ASTree right) | |||
throws CompileError | |||
{ | |||
if (!jvmJavaLangString.equals(cname)) | |||
badAssign(expr); | |||
convToString(type, dim); // the value might be null. | |||
right.accept(this); | |||
convToString(exprType, arrayDim); | |||
bytecode.addInvokevirtual(javaLangString, "concat", | |||
"(Ljava/lang/String;)Ljava/lang/String;"); | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = jvmJavaLangString; | |||
} | |||
private boolean invalidDim(int srcType, int srcDim, String srcClass, | |||
int destType, int destDim, String destClass, | |||
boolean isCast) | |||
@@ -727,15 +747,19 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int pc = bytecode.currentPc(); | |||
bytecode.addIndex(0); // correct later | |||
expr.thenExpr().accept(this); | |||
int dim1 = arrayDim; | |||
bytecode.addOpcode(Opcode.GOTO); | |||
int pc2 = bytecode.currentPc(); | |||
bytecode.addIndex(0); | |||
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); | |||
expr.elseExpr().accept(this); | |||
if (dim1 != arrayDim) | |||
throw new CompileError("type mismatch in ?:"); | |||
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); | |||
} | |||
private final int[] binOp = { | |||
static final int[] binOp = { | |||
'+', DADD, FADD, LADD, IADD, | |||
'-', DSUB, FSUB, LSUB, ISUB, | |||
'*', DMUL, FMUL, LMUL, IMUL, | |||
@@ -748,7 +772,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
RSHIFT, NOP, NOP, LSHR, ISHR, | |||
ARSHIFT, NOP, NOP, LUSHR, IUSHR }; | |||
private int lookupBinOp(int token) { | |||
static int lookupBinOp(int token) { | |||
int[] code = binOp; | |||
int s = code.length; | |||
for (int k = 0; k < s; k = k + 5) | |||
@@ -766,10 +790,14 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
int k = lookupBinOp(token); | |||
if (k >= 0) { | |||
expr.oprand1().accept(this); | |||
ASTree right = expr.oprand2(); | |||
if (right == null) | |||
return; // see TypeChecker.atBinExpr(). | |||
int type1 = exprType; | |||
int dim1 = arrayDim; | |||
String cname1 = className; | |||
expr.oprand2().accept(this); | |||
right.accept(this); | |||
if (dim1 != arrayDim) | |||
throw new CompileError("incompatible array types"); | |||
@@ -778,18 +806,17 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
atStringConcatExpr(expr, type1, dim1, cname1); | |||
else | |||
atArithBinExpr(expr, token, k, type1); | |||
return; | |||
} | |||
/* equation: &&, ||, ==, !=, <=, >=, <, > | |||
*/ | |||
booleanExpr(true, expr); | |||
bytecode.addIndex(7); | |||
bytecode.addIconst(0); // false | |||
bytecode.addOpcode(Opcode.GOTO); | |||
bytecode.addIndex(4); | |||
bytecode.addIconst(1); // true | |||
else { | |||
/* equation: &&, ||, ==, !=, <=, >=, <, > | |||
*/ | |||
booleanExpr(true, expr); | |||
bytecode.addIndex(7); | |||
bytecode.addIconst(0); // false | |||
bytecode.addOpcode(Opcode.GOTO); | |||
bytecode.addIndex(4); | |||
bytecode.addIconst(1); // true | |||
} | |||
} | |||
/* arrayDim values of the two oprands must be equal. | |||
@@ -846,6 +873,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
else | |||
bytecode.addOpcode(SWAP); | |||
// even if type1 is String, the left operand might be null. | |||
convToString(type1, dim1); | |||
bytecode.addOpcode(SWAP); | |||
@@ -923,6 +951,9 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
} | |||
else { // others | |||
expr.accept(this); | |||
if (exprType != BOOLEAN || arrayDim != 0) | |||
throw new CompileError("boolean expr is required"); | |||
bytecode.addOpcode(branchIf ? IFNE : IFEQ); | |||
} | |||
@@ -941,7 +972,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
return false; | |||
} | |||
private static int getCompOperator(ASTree expr) throws CompileError { | |||
static int getCompOperator(ASTree expr) throws CompileError { | |||
if (expr instanceof Expr) { | |||
Expr bexpr = (Expr)expr; | |||
int token = bexpr.getOperator(); | |||
@@ -969,19 +1000,19 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
return type1; | |||
} | |||
private final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, | |||
NEQ, IF_ICMPNE, IF_ICMPEQ, | |||
LE, IF_ICMPLE, IF_ICMPGT, | |||
GE, IF_ICMPGE, IF_ICMPLT, | |||
'<', IF_ICMPLT, IF_ICMPGE, | |||
'>', IF_ICMPGT, IF_ICMPLE }; | |||
private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, | |||
NEQ, IF_ICMPNE, IF_ICMPEQ, | |||
LE, IF_ICMPLE, IF_ICMPGT, | |||
GE, IF_ICMPGE, IF_ICMPLT, | |||
'<', IF_ICMPLT, IF_ICMPGE, | |||
'>', IF_ICMPGT, IF_ICMPLE }; | |||
private final int ifOp2[] = { EQ, IFEQ, IFNE, | |||
NEQ, IFNE, IFEQ, | |||
LE, IFLE, IFGT, | |||
GE, IFGE, IFLT, | |||
'<', IFLT, IFGE, | |||
'>', IFGT, IFLE }; | |||
private static final int ifOp2[] = { EQ, IFEQ, IFNE, | |||
NEQ, IFNE, IFEQ, | |||
LE, IFLE, IFGT, | |||
GE, IFGE, IFLT, | |||
'<', IFLT, IFGE, | |||
'>', IFGT, IFLE }; | |||
/* Produces the opcode to branch if the condition is true. | |||
* The oprands are not produced. | |||
@@ -1070,6 +1101,18 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT | |||
} | |||
// used in TypeChecker. | |||
static boolean isP_INT(int type) { | |||
return typePrecedence(type) == P_INT; | |||
} | |||
// used in TypeChecker. | |||
static boolean rightIsStrong(int type1, int type2) { | |||
int type1_p = typePrecedence(type1); | |||
int type2_p = typePrecedence(type2); | |||
return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p; | |||
} | |||
private static final int[] castOp = { | |||
/* D F L I */ | |||
/* double */ NOP, D2F, D2L, D2I, | |||
@@ -1221,14 +1264,12 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
} | |||
public void atExpr(Expr expr) throws CompileError { | |||
// method call, array access, member access, | |||
// array access, member access, | |||
// (unary) +, (unary) -, ++, --, !, ~ | |||
int token = expr.getOperator(); | |||
ASTree oprand = expr.oprand1(); | |||
if (token == CALL) // method call | |||
atMethodCall(expr); | |||
else if (token == '.') | |||
if (token == '.') | |||
if (((Symbol)expr.oprand2()).get().equals("length")) | |||
atArrayLength(expr); | |||
else | |||
@@ -1249,6 +1290,8 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
bytecode.addIndex(4); | |||
bytecode.addIconst(0); | |||
} | |||
else if (token == CALL) // method call | |||
fatal(); | |||
else { | |||
expr.oprand1().accept(this); | |||
int type = typePrecedence(exprType); | |||
@@ -1298,7 +1341,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
throw new CompileError("invalid type for " + expr.getName()); | |||
} | |||
protected abstract void atMethodCall(Expr expr) throws CompileError; | |||
public abstract void atCallExpr(CallExpr expr) throws CompileError; | |||
protected abstract void atFieldRead(ASTree expr) throws CompileError; | |||
@@ -1598,7 +1641,7 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { | |||
public void atStringL(StringL s) throws CompileError { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = "java/lang/String"; | |||
className = jvmJavaLangString; | |||
bytecode.addLdc(s.get()); | |||
} | |||
@@ -117,9 +117,9 @@ public class Javac { | |||
{ | |||
CtFieldWithInit f; | |||
Declarator d = fd.getDeclarator(); | |||
f = new CtFieldWithInit(gen.lookupClass(d), d.getVariable().get(), | |||
gen.getThisClass()); | |||
f.setModifiers(MemberCodeGen.getModifiers(fd.getModifiers())); | |||
f = new CtFieldWithInit(gen.resolver.lookupClass(d), | |||
d.getVariable().get(), gen.getThisClass()); | |||
f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); | |||
if (fd.getInit() != null) | |||
f.setInit(fd.getInit()); | |||
@@ -129,7 +129,7 @@ public class Javac { | |||
private CtMember compileMethod(Parser p, MethodDecl md) | |||
throws CompileError | |||
{ | |||
int mod = MemberCodeGen.getModifiers(md.getModifiers()); | |||
int mod = MemberResolver.getModifiers(md.getModifiers()); | |||
CtClass[] plist = gen.makeParamList(md); | |||
CtClass[] tlist = gen.makeThrowsList(md); | |||
recordParams(plist, Modifier.isStatic(mod)); | |||
@@ -147,7 +147,7 @@ public class Javac { | |||
} | |||
else { | |||
Declarator r = md.getReturn(); | |||
CtClass rtype = gen.lookupClass(r); | |||
CtClass rtype = gen.resolver.lookupClass(r); | |||
recordReturnType(rtype, false); | |||
CtMethod method = new CtMethod(rtype, r.getVariable().get(), | |||
plist, gen.getThisClass()); | |||
@@ -344,10 +344,22 @@ public class Javac { | |||
if (texpr != null) | |||
expr = Expr.make('.', texpr, expr); | |||
expr = Expr.make(TokenId.CALL, expr, args); | |||
expr.accept(gen); | |||
expr = CallExpr.makeCall(expr, args); | |||
gen.compileExpr(expr); | |||
gen.addNullIfVoid(); | |||
} | |||
public void setReturnType(JvstTypeChecker check, ASTList args) | |||
throws CompileError | |||
{ | |||
ASTree expr = new Member(m); | |||
if (texpr != null) | |||
expr = Expr.make('.', texpr, expr); | |||
expr = CallExpr.makeCall(expr, args); | |||
expr.accept(check); | |||
check.addNullIfVoid(); | |||
} | |||
}; | |||
gen.setProceedHandler(h, proceedName); | |||
@@ -374,10 +386,20 @@ public class Javac { | |||
{ | |||
Expr expr = Expr.make(TokenId.MEMBER, | |||
new Symbol(c), new Member(m)); | |||
expr = Expr.make(TokenId.CALL, expr, args); | |||
expr.accept(gen); | |||
expr = CallExpr.makeCall(expr, args); | |||
gen.compileExpr(expr); | |||
gen.addNullIfVoid(); | |||
} | |||
public void setReturnType(JvstTypeChecker check, ASTList args) | |||
throws CompileError | |||
{ | |||
Expr expr = Expr.make(TokenId.MEMBER, | |||
new Symbol(c), new Member(m)); | |||
expr = CallExpr.makeCall(expr, args); | |||
expr.accept(check); | |||
check.addNullIfVoid(); | |||
} | |||
}; | |||
gen.setProceedHandler(h, proceedName); | |||
@@ -408,9 +430,15 @@ public class Javac { | |||
public void doit(JvstCodeGen gen, Bytecode b, ASTList args) | |||
throws CompileError | |||
{ | |||
gen.compileInvokeSpecial(texpr, | |||
cname, method, desc, args); | |||
gen.compileInvokeSpecial(texpr, cname, method, desc, args); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.compileInvokeSpecial(texpr, cname, method, desc, args); | |||
} | |||
}; | |||
gen.setProceedHandler(h, proceedName); |
@@ -19,29 +19,31 @@ import javassist.*; | |||
import javassist.bytecode.*; | |||
import javassist.compiler.ast.*; | |||
/* Code generator methods for extensions by Javassist. | |||
/* Code generator accepting extended Java syntax for Javassist. | |||
*/ | |||
public class JvstCodeGen extends MemberCodeGen { | |||
private String paramArrayName = null; | |||
private String paramListName = null; | |||
private CtClass[] paramTypeList = null; | |||
String paramArrayName = null; | |||
String paramListName = null; | |||
CtClass[] paramTypeList = null; | |||
private int paramVarBase = 0; // variable index for $0 or $1. | |||
private boolean useParam0 = false; // true if $0 is used. | |||
private String param0Type = null; // JVM name | |||
private static final String sigName = "$sig"; | |||
private static final String dollarTypeName = "$type"; | |||
private static final String clazzName = "$class"; | |||
public static final String sigName = "$sig"; | |||
public static final String dollarTypeName = "$type"; | |||
public static final String clazzName = "$class"; | |||
private CtClass dollarType = null; | |||
private CtClass returnType = null; | |||
private String returnCastName = null; | |||
CtClass returnType = null; | |||
String returnCastName = null; | |||
private String returnVarName = null; // null if $_ is not used. | |||
private static final String wrapperCastName = "$w"; | |||
private String proceedName = null; | |||
private static final String cflowName = "$cflow"; | |||
private ProceedHandler procHandler = null; // null if not used. | |||
public static final String wrapperCastName = "$w"; | |||
String proceedName = null; | |||
public static final String cflowName = "$cflow"; | |||
ProceedHandler procHandler = null; // null if not used. | |||
public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) { | |||
super(b, cc, cp); | |||
setTypeChecker(new JvstTypeChecker(cc, cp, this)); | |||
} | |||
/* Index of $1. | |||
@@ -117,9 +119,6 @@ public class JvstCodeGen extends MemberCodeGen { | |||
className = "java/lang/Class"; | |||
} | |||
private void atSigOrType(String sig) throws CompileError { | |||
} | |||
protected void atFieldAssign(Expr expr, int op, ASTree left, | |||
ASTree right, boolean doDup) throws CompileError | |||
{ | |||
@@ -188,7 +187,8 @@ public class JvstCodeGen extends MemberCodeGen { | |||
compileUnwrapValue(returnType, bytecode); | |||
else if (returnType instanceof CtPrimitiveType) { | |||
CtPrimitiveType pt = (CtPrimitiveType)returnType; | |||
int destType = jvmTypeNameToExprType(pt.getDescriptor()); | |||
int destType = MemberResolver.jvmTypeNameToExprType( | |||
pt.getDescriptor()); | |||
atNumCastExpr(exprType, destType); | |||
exprType = destType; | |||
arrayDim = 0; | |||
@@ -203,7 +203,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
if (isRefType(exprType) || arrayDim > 0) | |||
return; // Object type. do nothing. | |||
CtClass clazz = lookupClass(exprType, arrayDim, className); | |||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); | |||
if (clazz instanceof CtPrimitiveType) { | |||
CtPrimitiveType pt = (CtPrimitiveType)clazz; | |||
String wrapper = pt.getWrapperName(); | |||
@@ -227,7 +227,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
/* Delegates to a ProcHandler object if the method call is | |||
* $proceed(). It may process $cflow(). | |||
*/ | |||
protected void atMethodCall(Expr expr) throws CompileError { | |||
public void atCallExpr(CallExpr expr) throws CompileError { | |||
ASTree method = expr.oprand1(); | |||
if (method instanceof Member) { | |||
String name = ((Member)method).get(); | |||
@@ -241,7 +241,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
} | |||
} | |||
super.atMethodCall(expr); | |||
super.atCallExpr(expr); | |||
} | |||
/* To support $cflow(). | |||
@@ -253,7 +253,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
makeCflowName(sbuf, cname.head()); | |||
String name = sbuf.toString(); | |||
Object[] names = classPool.lookupCflow(name); | |||
Object[] names = resolver.getClassPool().lookupCflow(name); | |||
if (names == null) | |||
throw new CompileError("no such a " + cflowName + ": " + name); | |||
@@ -413,7 +413,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
protected void atReturnStmnt(Stmnt st) throws CompileError { | |||
ASTree result = st.getLeft(); | |||
if (result != null && returnType == CtClass.voidType) { | |||
result.accept(this); | |||
compileExpr(result); | |||
if (is2word(exprType, arrayDim)) | |||
bytecode.addOpcode(POP2); | |||
else if (exprType != VOID) | |||
@@ -500,15 +500,15 @@ public class JvstCodeGen extends MemberCodeGen { | |||
paramVarBase = paramBase; | |||
useParam0 = use0; | |||
param0Type = jvmToJavaName(target); | |||
param0Type = MemberResolver.jvmToJavaName(target); | |||
inStaticMethod = isStatic; | |||
varNo = paramBase; | |||
if (use0) { | |||
String varName = prefix + "0"; | |||
Declarator decl | |||
= new Declarator(CLASS, javaToJvmName(target), 0, varNo++, | |||
new Symbol(varName)); | |||
= new Declarator(CLASS, MemberResolver.javaToJvmName(target), | |||
0, varNo++, new Symbol(varName)); | |||
tbl.append(varName, decl); | |||
} | |||
@@ -640,7 +640,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
private void setType(CtClass type, int dim) throws CompileError { | |||
if (type.isPrimitive()) { | |||
CtPrimitiveType pt = (CtPrimitiveType)type; | |||
exprType = descToType(pt.getDescriptor()); | |||
exprType = MemberResolver.descToType(pt.getDescriptor()); | |||
arrayDim = dim; | |||
className = null; | |||
} | |||
@@ -654,7 +654,7 @@ public class JvstCodeGen extends MemberCodeGen { | |||
else { | |||
exprType = CLASS; | |||
arrayDim = dim; | |||
className = javaToJvmName(type.getName()); | |||
className = MemberResolver.javaToJvmName(type.getName()); | |||
} | |||
} | |||
@@ -664,7 +664,8 @@ public class JvstCodeGen extends MemberCodeGen { | |||
if (arrayDim == 0 && !isRefType(exprType)) | |||
if (type instanceof CtPrimitiveType) { | |||
CtPrimitiveType pt = (CtPrimitiveType)type; | |||
atNumCastExpr(exprType, descToType(pt.getDescriptor())); | |||
atNumCastExpr(exprType, | |||
MemberResolver.descToType(pt.getDescriptor())); | |||
} | |||
else | |||
throw new CompileError("type mismatch"); |
@@ -0,0 +1,282 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.compiler; | |||
import javassist.*; | |||
import javassist.compiler.ast.*; | |||
/* Type checker accepting extended Java syntax for Javassist. | |||
*/ | |||
public class JvstTypeChecker extends TypeChecker { | |||
private JvstCodeGen codeGen; | |||
public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) { | |||
super(cc, cp); | |||
codeGen = gen; | |||
} | |||
/* If the type of the expression compiled last is void, | |||
* add ACONST_NULL and change exprType, arrayDim, className. | |||
*/ | |||
public void addNullIfVoid() { | |||
if (exprType == VOID) { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = jvmJavaLangObject; | |||
} | |||
} | |||
/* To support $args, $sig, and $type. | |||
* $args is an array of parameter list. | |||
*/ | |||
public void atMember(Member mem) throws CompileError { | |||
String name = mem.get(); | |||
if (name.equals(codeGen.paramArrayName)) { | |||
exprType = CLASS; | |||
arrayDim = 1; | |||
className = jvmJavaLangObject; | |||
} | |||
else if (name.equals(JvstCodeGen.sigName)) { | |||
exprType = CLASS; | |||
arrayDim = 1; | |||
className = "java/lang/Class"; | |||
} | |||
else if (name.equals(JvstCodeGen.dollarTypeName) | |||
|| name.equals(JvstCodeGen.clazzName)) { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = "java/lang/Class"; | |||
} | |||
else | |||
super.atMember(mem); | |||
} | |||
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) | |||
throws CompileError | |||
{ | |||
if (left instanceof Member | |||
&& ((Member)left).get().equals(codeGen.paramArrayName)) { | |||
right.accept(this); | |||
CtClass[] params = codeGen.paramTypeList; | |||
if (params == null) | |||
return; | |||
int n = params.length; | |||
for (int i = 0; i < n; ++i) | |||
compileUnwrapValue(params[i]); | |||
} | |||
else | |||
super.atFieldAssign(expr, op, left, right); | |||
} | |||
public void atCastExpr(CastExpr expr) throws CompileError { | |||
ASTList classname = expr.getClassName(); | |||
if (classname != null && expr.getArrayDim() == 0) { | |||
ASTree p = classname.head(); | |||
if (p instanceof Symbol && classname.tail() == null) { | |||
String typename = ((Symbol)p).get(); | |||
if (typename.equals(codeGen.returnCastName)) { | |||
atCastToRtype(expr); | |||
return; | |||
} | |||
else if (typename.equals(JvstCodeGen.wrapperCastName)) { | |||
atCastToWrapper(expr); | |||
return; | |||
} | |||
} | |||
} | |||
super.atCastExpr(expr); | |||
} | |||
/** | |||
* Inserts a cast operator to the return type. | |||
* If the return type is void, this does nothing. | |||
*/ | |||
protected void atCastToRtype(CastExpr expr) throws CompileError { | |||
CtClass returnType = codeGen.returnType; | |||
expr.getOprand().accept(this); | |||
if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) | |||
compileUnwrapValue(returnType); | |||
else if (returnType instanceof CtPrimitiveType) { | |||
CtPrimitiveType pt = (CtPrimitiveType)returnType; | |||
int destType = MemberResolver.jvmTypeNameToExprType( | |||
pt.getDescriptor()); | |||
exprType = destType; | |||
arrayDim = 0; | |||
className = null; | |||
} | |||
} | |||
protected void atCastToWrapper(CastExpr expr) throws CompileError { | |||
expr.getOprand().accept(this); | |||
if (CodeGen.isRefType(exprType) || arrayDim > 0) | |||
return; // Object type. do nothing. | |||
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); | |||
if (clazz instanceof CtPrimitiveType) { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = jvmJavaLangObject; | |||
} | |||
} | |||
/* Delegates to a ProcHandler object if the method call is | |||
* $proceed(). It may process $cflow(). | |||
*/ | |||
public void atCallExpr(CallExpr expr) throws CompileError { | |||
ASTree method = expr.oprand1(); | |||
if (method instanceof Member) { | |||
String name = ((Member)method).get(); | |||
if (codeGen.procHandler != null | |||
&& name.equals(codeGen.proceedName)) { | |||
codeGen.procHandler.setReturnType(this, | |||
(ASTList)expr.oprand2()); | |||
return; | |||
} | |||
else if (name.equals(JvstCodeGen.cflowName)) { | |||
atCflow((ASTList)expr.oprand2()); | |||
return; | |||
} | |||
} | |||
super.atCallExpr(expr); | |||
} | |||
/* To support $cflow(). | |||
*/ | |||
protected void atCflow(ASTList cname) throws CompileError { | |||
exprType = INT; | |||
arrayDim = 0; | |||
className = null; | |||
} | |||
/* To support $$. ($$) is equivalent to ($1, ..., $n). | |||
* It can be used only as a parameter list of method call. | |||
*/ | |||
public boolean isParamListName(ASTList args) { | |||
if (codeGen.paramTypeList != null | |||
&& args != null && args.tail() == null) { | |||
ASTree left = args.head(); | |||
return (left instanceof Member | |||
&& ((Member)left).get().equals(codeGen.paramListName)); | |||
} | |||
else | |||
return false; | |||
} | |||
public int getMethodArgsLength(ASTList args) { | |||
String pname = codeGen.paramListName; | |||
int n = 0; | |||
while (args != null) { | |||
ASTree a = args.head(); | |||
if (a instanceof Member && ((Member)a).get().equals(pname)) { | |||
if (codeGen.paramTypeList != null) | |||
n += codeGen.paramTypeList.length; | |||
} | |||
else | |||
++n; | |||
args = args.tail(); | |||
} | |||
return n; | |||
} | |||
public void atMethodArgs(ASTList args, int[] types, int[] dims, | |||
String[] cnames) throws CompileError { | |||
CtClass[] params = codeGen.paramTypeList; | |||
String pname = codeGen.paramListName; | |||
int i = 0; | |||
while (args != null) { | |||
ASTree a = args.head(); | |||
if (a instanceof Member && ((Member)a).get().equals(pname)) { | |||
if (params != null) { | |||
int n = params.length; | |||
for (int k = 0; k < n; ++k) { | |||
CtClass p = params[k]; | |||
setType(p); | |||
types[i] = exprType; | |||
dims[i] = arrayDim; | |||
cnames[i] = className; | |||
++i; | |||
} | |||
} | |||
} | |||
else { | |||
a.accept(this); | |||
types[i] = exprType; | |||
dims[i] = arrayDim; | |||
cnames[i] = className; | |||
++i; | |||
} | |||
args = args.tail(); | |||
} | |||
} | |||
/* called by Javac#recordSpecialProceed(). | |||
*/ | |||
void compileInvokeSpecial(ASTree target, String classname, | |||
String methodname, String descriptor, | |||
ASTList args) | |||
throws CompileError | |||
{ | |||
target.accept(this); | |||
int nargs = getMethodArgsLength(args); | |||
atMethodArgs(args, new int[nargs], new int[nargs], | |||
new String[nargs]); | |||
setReturnType(descriptor); | |||
addNullIfVoid(); | |||
} | |||
protected void compileUnwrapValue(CtClass type) throws CompileError | |||
{ | |||
if (type == CtClass.voidType) | |||
addNullIfVoid(); | |||
else | |||
setType(type); | |||
} | |||
/* Sets exprType, arrayDim, and className; | |||
* If type is void, then this method does nothing. | |||
*/ | |||
public void setType(CtClass type) throws CompileError { | |||
setType(type, 0); | |||
} | |||
private void setType(CtClass type, int dim) throws CompileError { | |||
if (type.isPrimitive()) { | |||
CtPrimitiveType pt = (CtPrimitiveType)type; | |||
exprType = MemberResolver.descToType(pt.getDescriptor()); | |||
arrayDim = dim; | |||
className = null; | |||
} | |||
else if (type.isArray()) | |||
try { | |||
setType(type.getComponentType(), dim + 1); | |||
} | |||
catch (NotFoundException e) { | |||
throw new CompileError("undefined type: " + type.getName()); | |||
} | |||
else { | |||
exprType = CLASS; | |||
arrayDim = dim; | |||
className = MemberResolver.javaToJvmName(type.getName()); | |||
} | |||
} | |||
} |
@@ -15,7 +15,6 @@ | |||
package javassist.compiler; | |||
import java.util.List; | |||
import javassist.*; | |||
import javassist.bytecode.*; | |||
import javassist.compiler.ast.*; | |||
@@ -23,15 +22,16 @@ import javassist.compiler.ast.*; | |||
/* Code generator methods depending on javassist.* classes. | |||
*/ | |||
public class MemberCodeGen extends CodeGen { | |||
protected ClassPool classPool; | |||
protected MemberResolver resolver; | |||
protected CtClass thisClass; | |||
protected MethodInfo thisMethod; | |||
protected boolean resultStatic; | |||
public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { | |||
public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) | |||
{ | |||
super(b); | |||
classPool = cp; | |||
resolver = new MemberResolver(cp); | |||
thisClass = cc; | |||
thisMethod = null; | |||
} | |||
@@ -41,6 +41,8 @@ public class MemberCodeGen extends CodeGen { | |||
*/ | |||
public void setThisMethod(CtMethod m) { | |||
thisMethod = m.getMethodInfo2(); | |||
if (typeChecker != null) | |||
typeChecker.setThisMethod(thisMethod); | |||
} | |||
public CtClass getThisClass() { return thisClass; } | |||
@@ -49,19 +51,21 @@ public class MemberCodeGen extends CodeGen { | |||
* Returns the JVM-internal representation of this class name. | |||
*/ | |||
protected String getThisName() { | |||
return javaToJvmName(thisClass.getName()); | |||
return MemberResolver.javaToJvmName(thisClass.getName()); | |||
} | |||
/** | |||
* Returns the JVM-internal representation of this super class name. | |||
*/ | |||
protected String getSuperName() throws CompileError { | |||
return javaToJvmName(getSuperclass(thisClass).getName()); | |||
return MemberResolver.javaToJvmName( | |||
MemberResolver.getSuperclass(thisClass).getName()); | |||
} | |||
protected void insertDefaultSuperCall() throws CompileError { | |||
bytecode.addAload(0); | |||
bytecode.addInvokespecial(getSuperclass(thisClass), "<init>", "()V"); | |||
bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), | |||
"<init>", "()V"); | |||
} | |||
protected void atTryStmnt(Stmnt st) throws CompileError { | |||
@@ -90,8 +94,8 @@ public class MemberCodeGen extends CodeGen { | |||
decl.setLocalVar(var); | |||
CtClass type = lookupJvmClass(decl.getClassName()); | |||
decl.setClassName(javaToJvmName(type.getName())); | |||
CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); | |||
decl.setClassName(MemberResolver.javaToJvmName(type.getName())); | |||
bytecode.addExceptionHandler(start, end, bytecode.currentPc(), | |||
type); | |||
bytecode.growStack(1); | |||
@@ -116,17 +120,18 @@ public class MemberCodeGen extends CodeGen { | |||
if (expr.isArray()) | |||
atNewArrayExpr(expr); | |||
else { | |||
CtClass clazz = lookupClass(expr.getClassName()); | |||
CtClass clazz = resolver.lookupClassByName(expr.getClassName()); | |||
String cname = clazz.getName(); | |||
ASTList args = expr.getArguments(); | |||
bytecode.addNew(cname); | |||
bytecode.addOpcode(DUP); | |||
atMethodCall2(clazz, MethodInfo.nameInit, args, false, true, -1); | |||
atMethodCallCore(clazz, MethodInfo.nameInit, args, | |||
false, true, -1, null); | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = javaToJvmName(cname); | |||
className = MemberResolver.javaToJvmName(cname); | |||
} | |||
} | |||
@@ -147,7 +152,7 @@ public class MemberCodeGen extends CodeGen { | |||
arrayDim = 1; | |||
if (type == CLASS) { | |||
className = resolveClassName(classname); | |||
bytecode.addAnewarray(jvmToJavaName(className)); | |||
bytecode.addAnewarray(MemberResolver.jvmToJavaName(className)); | |||
} | |||
else { | |||
className = null; | |||
@@ -220,7 +225,7 @@ public class MemberCodeGen extends CodeGen { | |||
bytecode.addMultiNewarray(desc, count); | |||
} | |||
protected void atMethodCall(Expr expr) throws CompileError { | |||
public void atCallExpr(CallExpr expr) throws CompileError { | |||
String mname = null; | |||
CtClass targetClass = null; | |||
ASTree method = expr.oprand1(); | |||
@@ -229,10 +234,11 @@ public class MemberCodeGen extends CodeGen { | |||
boolean isSpecial = false; | |||
int aload0pos = -1; | |||
MemberResolver.Method cached = expr.getMethod(); | |||
if (method instanceof Member) { | |||
mname = ((Member)method).get(); | |||
targetClass = thisClass; | |||
if (inStaticMethod) | |||
if (inStaticMethod || (cached != null && cached.isStatic())) | |||
isStatic = true; // should be static | |||
else { | |||
aload0pos = bytecode.currentPc(); | |||
@@ -249,14 +255,15 @@ public class MemberCodeGen extends CodeGen { | |||
bytecode.addAload(0); // this | |||
if (((Keyword)method).get() == SUPER) | |||
targetClass = getSuperclass(targetClass); | |||
targetClass = MemberResolver.getSuperclass(targetClass); | |||
} | |||
else if (method instanceof Expr) { | |||
Expr e = (Expr)method; | |||
mname = ((Symbol)e.oprand2()).get(); | |||
int op = e.getOperator(); | |||
if (op == MEMBER) { // static method | |||
targetClass = lookupJavaClass(((Symbol)e.oprand1()).get()); | |||
targetClass | |||
= resolver.lookupClass(((Symbol)e.oprand1()).get()); | |||
isStatic = true; | |||
} | |||
else if (op == '.') { | |||
@@ -280,9 +287,9 @@ public class MemberCodeGen extends CodeGen { | |||
} | |||
if (arrayDim > 0) | |||
targetClass = lookupJavaClass(javaLangObject); | |||
targetClass = resolver.lookupClass(javaLangObject); | |||
else if (exprType == CLASS /* && arrayDim == 0 */) | |||
targetClass = lookupJvmClass(className); | |||
targetClass = resolver.lookupClassByJvmName(className); | |||
else | |||
badMethod(); | |||
} | |||
@@ -292,27 +299,18 @@ public class MemberCodeGen extends CodeGen { | |||
else | |||
fatal(); | |||
atMethodCall2(targetClass, mname, args, isStatic, isSpecial, | |||
aload0pos); | |||
atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, | |||
aload0pos, cached); | |||
} | |||
private static void badMethod() throws CompileError { | |||
throw new CompileError("bad method"); | |||
} | |||
private static CtClass getSuperclass(CtClass c) throws CompileError { | |||
try { | |||
return c.getSuperclass(); | |||
} | |||
catch (NotFoundException e) { | |||
throw new CompileError("cannot find the super class of " | |||
+ c.getName()); | |||
} | |||
} | |||
public void atMethodCall2(CtClass targetClass, String mname, | |||
// atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew | |||
public void atMethodCallCore(CtClass targetClass, String mname, | |||
ASTList args, boolean isStatic, boolean isSpecial, | |||
int aload0pos) | |||
int aload0pos, MemberResolver.Method found) | |||
throws CompileError | |||
{ | |||
int nargs = getMethodArgsLength(args); | |||
@@ -320,6 +318,11 @@ public class MemberCodeGen extends CodeGen { | |||
int[] dims = new int[nargs]; | |||
String[] cnames = new String[nargs]; | |||
if (!isStatic && found != null && found.isStatic()) { | |||
bytecode.addOpcode(POP); | |||
isStatic = true; | |||
} | |||
int stack = bytecode.getStackDepth(); | |||
atMethodArgs(args, types, dims, cnames); | |||
@@ -327,8 +330,10 @@ public class MemberCodeGen extends CodeGen { | |||
// used by invokeinterface | |||
int count = bytecode.getStackDepth() - stack + 1; | |||
Object[] found = lookupMethod(targetClass, thisMethod, mname, | |||
types, dims, cnames, false); | |||
if (found == null) | |||
found = resolver.lookupMethod(targetClass, thisMethod, mname, | |||
types, dims, cnames, false); | |||
if (found == null) { | |||
String msg; | |||
if (mname.equals(MethodInfo.nameInit)) | |||
@@ -340,8 +345,8 @@ public class MemberCodeGen extends CodeGen { | |||
throw new CompileError(msg); | |||
} | |||
CtClass declClass = (CtClass)found[0]; | |||
MethodInfo minfo = (MethodInfo)found[1]; | |||
CtClass declClass = found.declaring; | |||
MethodInfo minfo = found.info; | |||
String desc = minfo.getDescriptor(); | |||
int acc = minfo.getAccessFlags(); | |||
@@ -428,7 +433,7 @@ public class MemberCodeGen extends CodeGen { | |||
className = desc.substring(i + 1, j); | |||
} | |||
else { | |||
exprType = descToType(c); | |||
exprType = MemberResolver.descToType(c); | |||
className = null; | |||
} | |||
@@ -450,188 +455,6 @@ public class MemberCodeGen extends CodeGen { | |||
} | |||
} | |||
private Object[] lookupMethod(CtClass clazz, MethodInfo current, | |||
String methodName, | |||
int[] argTypes, int[] argDims, | |||
String[] argClassNames, boolean onlyExact) | |||
throws CompileError | |||
{ | |||
Object[] maybe = null; | |||
if (current != null) | |||
if (current.getName().equals(methodName)) { | |||
int res = compareSignature(current.getDescriptor(), | |||
argTypes, argDims, argClassNames); | |||
Object[] r = new Object[] { clazz, current }; | |||
if (res == YES) | |||
return r; | |||
else if (res == MAYBE && maybe == null) | |||
maybe = r; | |||
} | |||
List list = clazz.getClassFile2().getMethods(); | |||
int n = list.size(); | |||
for (int i = 0; i < n; ++i) { | |||
MethodInfo minfo = (MethodInfo)list.get(i); | |||
if (minfo.getName().equals(methodName)) { | |||
int res = compareSignature(minfo.getDescriptor(), | |||
argTypes, argDims, argClassNames); | |||
Object[] r = new Object[] { clazz, minfo }; | |||
if (res == YES) | |||
return r; | |||
else if (res == MAYBE && maybe == null) | |||
maybe = r; | |||
} | |||
} | |||
try { | |||
CtClass pclazz = clazz.getSuperclass(); | |||
if (pclazz != null) { | |||
Object[] r = lookupMethod(pclazz, null, methodName, argTypes, | |||
argDims, argClassNames, | |||
(onlyExact || maybe != null)); | |||
if (r != null) | |||
return r; | |||
} | |||
} | |||
catch (NotFoundException e) {} | |||
/* -- not necessary to search implemented interfaces. | |||
try { | |||
CtClass[] ifs = clazz.getInterfaces(); | |||
int size = ifs.length; | |||
for (int i = 0; i < size; ++i) { | |||
Object[] r = lookupMethod(ifs[i], methodName, argTypes, | |||
argDims, argClassNames); | |||
if (r != null) | |||
return r; | |||
} | |||
} | |||
catch (NotFoundException e) {} | |||
*/ | |||
if (onlyExact) | |||
return null; | |||
else | |||
return maybe; | |||
} | |||
private static final int YES = 2; | |||
private static final int MAYBE = 1; | |||
private static final int NO = 0; | |||
/* | |||
* Returns YES if actual parameter types matches the given signature. | |||
* | |||
* argTypes, argDims, and argClassNames represent actual parameters. | |||
* | |||
* This method does not correctly implement the Java method dispatch | |||
* algorithm. | |||
*/ | |||
private int compareSignature(String desc, int[] argTypes, | |||
int[] argDims, String[] argClassNames) | |||
throws CompileError | |||
{ | |||
int result = YES; | |||
int i = 1; | |||
int nArgs = argTypes.length; | |||
if (nArgs != Descriptor.numOfParameters(desc)) | |||
return NO; | |||
int len = desc.length(); | |||
for (int n = 0; i < len; ++n) { | |||
char c = desc.charAt(i++); | |||
if (c == ')') | |||
return (n == nArgs ? result : NO); | |||
else if (n >= nArgs) | |||
return NO; | |||
int dim = 0; | |||
while (c == '[') { | |||
++dim; | |||
c = desc.charAt(i++); | |||
} | |||
if (argTypes[n] == NULL) { | |||
if (dim == 0 && c != 'L') | |||
return NO; | |||
} | |||
else if (argDims[n] != dim) { | |||
if (!(dim == 0 && c == 'L' | |||
&& desc.startsWith("java/lang/Object;", i))) | |||
return NO; | |||
// if the thread reaches here, c must be 'L'. | |||
i = desc.indexOf(';', i) + 1; | |||
result = MAYBE; | |||
if (i <= 0) | |||
return NO; // invalid descriptor? | |||
} | |||
else if (c == 'L') { // not compare | |||
int j = desc.indexOf(';', i); | |||
if (j < 0 || argTypes[n] != CLASS) | |||
return NO; | |||
String cname = desc.substring(i, j); | |||
if (!cname.equals(argClassNames[n])) { | |||
CtClass clazz = lookupJvmClass(argClassNames[n]); | |||
try { | |||
if (clazz.subtypeOf(lookupJvmClass(cname))) | |||
result = MAYBE; | |||
else | |||
return NO; | |||
} | |||
catch (NotFoundException e) { | |||
result = MAYBE; // should be NO? | |||
} | |||
} | |||
i = j + 1; | |||
} | |||
else { | |||
int t = descToType(c); | |||
int at = argTypes[n]; | |||
if (t != at) | |||
if (t == INT | |||
&& (at == SHORT || at == BYTE || at == CHAR)) | |||
result = MAYBE; | |||
else | |||
return NO; | |||
} | |||
} | |||
return NO; | |||
} | |||
protected static int descToType(char c) throws CompileError { | |||
switch (c) { | |||
case 'Z' : | |||
return BOOLEAN; | |||
case 'C' : | |||
return CHAR; | |||
case 'B' : | |||
return BYTE; | |||
case 'S' : | |||
return SHORT; | |||
case 'I' : | |||
return INT; | |||
case 'J' : | |||
return LONG; | |||
case 'F' : | |||
return FLOAT; | |||
case 'D' : | |||
return DOUBLE; | |||
case 'V' : | |||
return VOID; | |||
case 'L' : | |||
case '[' : | |||
return CLASS; | |||
default : | |||
fatal(); | |||
return VOID; | |||
} | |||
} | |||
protected void atFieldAssign(Expr expr, int op, ASTree left, | |||
ASTree right, boolean doDup) throws CompileError | |||
{ | |||
@@ -704,7 +527,7 @@ public class MemberCodeGen extends CodeGen { | |||
arrayDim = dim; | |||
boolean is2byte = (c == 'J' || c == 'D'); | |||
exprType = descToType(c); | |||
exprType = MemberResolver.descToType(c); | |||
if (c == 'L') | |||
className = type.substring(i + 1, type.indexOf(';', i + 1)); | |||
@@ -795,15 +618,16 @@ public class MemberCodeGen extends CodeGen { | |||
Expr e = (Expr)expr; | |||
int op = e.getOperator(); | |||
if (op == MEMBER) { | |||
f = lookupJavaField(((Symbol)e.oprand1()).get(), | |||
(Symbol)e.oprand2()); | |||
f = resolver.lookupField(((Symbol)e.oprand1()).get(), | |||
(Symbol)e.oprand2()); | |||
is_static = true; | |||
} | |||
else if (op == '.') { | |||
try { | |||
e.oprand1().accept(this); | |||
if (exprType == CLASS && arrayDim == 0) | |||
f = lookupJvmField(className, (Symbol)e.oprand2()); | |||
f = resolver.lookupFieldByJvmName(className, | |||
(Symbol)e.oprand2()); | |||
else | |||
badLvalue(); | |||
@@ -818,7 +642,8 @@ public class MemberCodeGen extends CodeGen { | |||
Symbol fname = (Symbol)e.oprand2(); | |||
// it should be a static field. | |||
try { | |||
f = lookupJvmField(nfe.getField(), fname); | |||
f = resolver.lookupFieldByJvmName(nfe.getField(), | |||
fname); | |||
is_static = true; | |||
} | |||
catch (CompileError ce) { | |||
@@ -851,7 +676,7 @@ public class MemberCodeGen extends CodeGen { | |||
int i = 0; | |||
params = new CtClass[plist.length()]; | |||
while (plist != null) { | |||
params[i++] = lookupClass((Declarator)plist.head()); | |||
params[i++] = resolver.lookupClass((Declarator)plist.head()); | |||
plist = plist.tail(); | |||
} | |||
} | |||
@@ -868,7 +693,7 @@ public class MemberCodeGen extends CodeGen { | |||
int i = 0; | |||
clist = new CtClass[list.length()]; | |||
while (list != null) { | |||
clist[i++] = lookupClass((ASTList)list.head()); | |||
clist[i++] = resolver.lookupClassByName((ASTList)list.head()); | |||
list = list.tail(); | |||
} | |||
@@ -876,184 +701,19 @@ public class MemberCodeGen extends CodeGen { | |||
} | |||
} | |||
public static int getModifiers(ASTList mods) { | |||
int m = 0; | |||
while (mods != null) { | |||
Keyword k = (Keyword)mods.head(); | |||
mods = mods.tail(); | |||
switch (k.get()) { | |||
case STATIC : | |||
m |= Modifier.STATIC; | |||
break; | |||
case FINAL : | |||
m |= Modifier.FINAL; | |||
break; | |||
case SYNCHRONIZED : | |||
m |= Modifier.SYNCHRONIZED; | |||
break; | |||
case ABSTRACT : | |||
m |= Modifier.ABSTRACT; | |||
break; | |||
case PUBLIC : | |||
m |= Modifier.PUBLIC; | |||
break; | |||
case PROTECTED : | |||
m |= Modifier.PROTECTED; | |||
break; | |||
case PRIVATE : | |||
m |= Modifier.PRIVATE; | |||
break; | |||
case VOLATILE : | |||
m |= Modifier.VOLATILE; | |||
break; | |||
case TRANSIENT : | |||
m |= Modifier.TRANSIENT; | |||
break; | |||
case STRICT : | |||
m |= Modifier.STRICT; | |||
break; | |||
} | |||
} | |||
return m; | |||
} | |||
/* Converts a class name into a JVM-internal representation. | |||
* | |||
* It may also expand a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
protected String resolveClassName(ASTList name) throws CompileError { | |||
if (name == null) | |||
return null; | |||
else | |||
return javaToJvmName(lookupClass(name).getName()); | |||
return resolver.resolveClassName(name); | |||
} | |||
/* Expands a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
protected String resolveClassName(String jvmName) throws CompileError { | |||
if (jvmName == null) | |||
return null; | |||
else | |||
return javaToJvmName(lookupJvmClass(jvmName).getName()); | |||
} | |||
protected CtClass lookupClass(Declarator decl) throws CompileError { | |||
return lookupClass(decl.getType(), decl.getArrayDim(), | |||
decl.getClassName()); | |||
} | |||
protected CtClass lookupClass(int type, int dim, String classname) | |||
throws CompileError | |||
{ | |||
String cname = ""; | |||
CtClass clazz; | |||
switch (type) { | |||
case CLASS : | |||
clazz = lookupJvmClass(classname); | |||
if (dim > 0) | |||
cname = clazz.getName(); | |||
else | |||
return clazz; | |||
break; | |||
case BOOLEAN : | |||
cname = "boolean"; | |||
break; | |||
case CHAR : | |||
cname = "char"; | |||
break; | |||
case BYTE : | |||
cname = "byte"; | |||
break; | |||
case SHORT : | |||
cname = "short"; | |||
break; | |||
case INT : | |||
cname = "int"; | |||
break; | |||
case LONG : | |||
cname = "long"; | |||
break; | |||
case FLOAT : | |||
cname = "float"; | |||
break; | |||
case DOUBLE : | |||
cname = "double"; | |||
break; | |||
case VOID : | |||
cname = "void"; | |||
break; | |||
default : | |||
fatal(); | |||
} | |||
while (dim-- > 0) | |||
cname += "[]"; | |||
return lookupJavaClass(cname); | |||
} | |||
protected CtClass lookupClass(ASTList name) throws CompileError { | |||
return lookupJavaClass(Declarator.astToClassName(name, '.')); | |||
} | |||
protected CtClass lookupJvmClass(String jvmName) throws CompileError { | |||
return lookupJavaClass(jvmToJavaName(jvmName)); | |||
} | |||
/** | |||
* @param name a qualified class name. e.g. java.lang.String | |||
*/ | |||
private CtClass lookupJavaClass(String name) throws CompileError { | |||
try { | |||
return classPool.get(name); | |||
} | |||
catch (NotFoundException e) {} | |||
try { | |||
if (name.indexOf('.') < 0) | |||
return classPool.get("java.lang." + name); | |||
} | |||
catch (NotFoundException e) {} | |||
throw new CompileError("no such class: " + name); | |||
} | |||
public CtField lookupField(ASTList className, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
return lookupJavaField(Declarator.astToClassName(className, '.'), | |||
fieldName); | |||
} | |||
public CtField lookupJvmField(String className, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
return lookupJavaField(jvmToJavaName(className), fieldName); | |||
} | |||
/** | |||
* @param name a qualified class name. e.g. java.lang.String | |||
*/ | |||
private CtField lookupJavaField(String className, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
CtClass cc = lookupJavaClass(className); | |||
try { | |||
return cc.getField(fieldName.get()); | |||
} | |||
catch (NotFoundException e) {} | |||
throw new CompileError("no such field: " + fieldName.get()); | |||
} | |||
protected static String javaToJvmName(String classname) { | |||
return classname.replace('.', '/'); | |||
} | |||
protected static String jvmToJavaName(String classname) { | |||
return classname.replace('/', '.'); | |||
return resolver.resolveJvmClassName(jvmName); | |||
} | |||
} |
@@ -0,0 +1,461 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.compiler; | |||
import java.util.List; | |||
import javassist.*; | |||
import javassist.bytecode.*; | |||
import javassist.compiler.ast.*; | |||
/* Code generator methods depending on javassist.* classes. | |||
*/ | |||
public class MemberResolver implements TokenId { | |||
private ClassPool classPool; | |||
public MemberResolver(ClassPool cp) { | |||
classPool = cp; | |||
} | |||
public ClassPool getClassPool() { return classPool; } | |||
private static void fatal() throws CompileError { | |||
throw new CompileError("fatal"); | |||
} | |||
public static class Method { | |||
public CtClass declaring; | |||
public MethodInfo info; | |||
public Method(CtClass c, MethodInfo i) { | |||
declaring = c; | |||
info = i; | |||
} | |||
/** | |||
* Returns true if the invoked method is static. | |||
*/ | |||
public boolean isStatic() { | |||
int acc = info.getAccessFlags(); | |||
return (acc & AccessFlag.STATIC) != 0; | |||
} | |||
} | |||
public Method lookupMethod(CtClass clazz, MethodInfo current, | |||
String methodName, | |||
int[] argTypes, int[] argDims, | |||
String[] argClassNames, boolean onlyExact) | |||
throws CompileError | |||
{ | |||
Method maybe = null; | |||
// to enable the creation of a recursively called method | |||
if (current != null) | |||
if (current.getName().equals(methodName)) { | |||
int res = compareSignature(current.getDescriptor(), | |||
argTypes, argDims, argClassNames); | |||
Method r = new Method(clazz, current); | |||
if (res == YES) | |||
return r; | |||
else if (res == MAYBE && maybe == null) | |||
maybe = r; | |||
} | |||
List list = clazz.getClassFile2().getMethods(); | |||
int n = list.size(); | |||
for (int i = 0; i < n; ++i) { | |||
MethodInfo minfo = (MethodInfo)list.get(i); | |||
if (minfo.getName().equals(methodName)) { | |||
int res = compareSignature(minfo.getDescriptor(), | |||
argTypes, argDims, argClassNames); | |||
Method r = new Method(clazz, minfo); | |||
if (res == YES) | |||
return r; | |||
else if (res == MAYBE && maybe == null) | |||
maybe = r; | |||
} | |||
} | |||
try { | |||
CtClass pclazz = clazz.getSuperclass(); | |||
if (pclazz != null) { | |||
Method r = lookupMethod(pclazz, null, methodName, argTypes, | |||
argDims, argClassNames, | |||
(onlyExact || maybe != null)); | |||
if (r != null) | |||
return r; | |||
} | |||
} | |||
catch (NotFoundException e) {} | |||
/* -- not necessary to search implemented interfaces. | |||
try { | |||
CtClass[] ifs = clazz.getInterfaces(); | |||
int size = ifs.length; | |||
for (int i = 0; i < size; ++i) { | |||
Object[] r = lookupMethod(ifs[i], methodName, argTypes, | |||
argDims, argClassNames); | |||
if (r != null) | |||
return r; | |||
} | |||
} | |||
catch (NotFoundException e) {} | |||
*/ | |||
if (onlyExact) | |||
return null; | |||
else | |||
return maybe; | |||
} | |||
private static final int YES = 2; | |||
private static final int MAYBE = 1; | |||
private static final int NO = 0; | |||
/* | |||
* Returns YES if actual parameter types matches the given signature. | |||
* | |||
* argTypes, argDims, and argClassNames represent actual parameters. | |||
* | |||
* This method does not correctly implement the Java method dispatch | |||
* algorithm. | |||
*/ | |||
private int compareSignature(String desc, int[] argTypes, | |||
int[] argDims, String[] argClassNames) | |||
throws CompileError | |||
{ | |||
int result = YES; | |||
int i = 1; | |||
int nArgs = argTypes.length; | |||
if (nArgs != Descriptor.numOfParameters(desc)) | |||
return NO; | |||
int len = desc.length(); | |||
for (int n = 0; i < len; ++n) { | |||
char c = desc.charAt(i++); | |||
if (c == ')') | |||
return (n == nArgs ? result : NO); | |||
else if (n >= nArgs) | |||
return NO; | |||
int dim = 0; | |||
while (c == '[') { | |||
++dim; | |||
c = desc.charAt(i++); | |||
} | |||
if (argTypes[n] == NULL) { | |||
if (dim == 0 && c != 'L') | |||
return NO; | |||
} | |||
else if (argDims[n] != dim) { | |||
if (!(dim == 0 && c == 'L' | |||
&& desc.startsWith("java/lang/Object;", i))) | |||
return NO; | |||
// if the thread reaches here, c must be 'L'. | |||
i = desc.indexOf(';', i) + 1; | |||
result = MAYBE; | |||
if (i <= 0) | |||
return NO; // invalid descriptor? | |||
} | |||
else if (c == 'L') { // not compare | |||
int j = desc.indexOf(';', i); | |||
if (j < 0 || argTypes[n] != CLASS) | |||
return NO; | |||
String cname = desc.substring(i, j); | |||
if (!cname.equals(argClassNames[n])) { | |||
CtClass clazz = lookupClassByJvmName(argClassNames[n]); | |||
try { | |||
if (clazz.subtypeOf(lookupClassByJvmName(cname))) | |||
result = MAYBE; | |||
else | |||
return NO; | |||
} | |||
catch (NotFoundException e) { | |||
result = MAYBE; // should be NO? | |||
} | |||
} | |||
i = j + 1; | |||
} | |||
else { | |||
int t = descToType(c); | |||
int at = argTypes[n]; | |||
if (t != at) | |||
if (t == INT | |||
&& (at == SHORT || at == BYTE || at == CHAR)) | |||
result = MAYBE; | |||
else | |||
return NO; | |||
} | |||
} | |||
return NO; | |||
} | |||
/** | |||
* @param jvmClassName a JVM class name. e.g. java/lang/String | |||
*/ | |||
public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
return lookupField(jvmToJavaName(jvmClassName), fieldName); | |||
} | |||
// never used?? | |||
private CtField lookupField2(ASTList className, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
return lookupField(Declarator.astToClassName(className, '.'), | |||
fieldName); | |||
} | |||
/** | |||
* @param name a qualified class name. e.g. java.lang.String | |||
*/ | |||
public CtField lookupField(String className, Symbol fieldName) | |||
throws CompileError | |||
{ | |||
CtClass cc = lookupClass(className); | |||
try { | |||
return cc.getField(fieldName.get()); | |||
} | |||
catch (NotFoundException e) {} | |||
throw new CompileError("no such field: " + fieldName.get()); | |||
} | |||
public CtClass lookupClassByName(ASTList name) throws CompileError { | |||
return lookupClass(Declarator.astToClassName(name, '.')); | |||
} | |||
public CtClass lookupClassByJvmName(String jvmName) throws CompileError { | |||
return lookupClass(jvmToJavaName(jvmName)); | |||
} | |||
public CtClass lookupClass(Declarator decl) throws CompileError { | |||
return lookupClass(decl.getType(), decl.getArrayDim(), | |||
decl.getClassName()); | |||
} | |||
/** | |||
* @parma classname jvm class name. | |||
*/ | |||
public CtClass lookupClass(int type, int dim, String classname) | |||
throws CompileError | |||
{ | |||
String cname = ""; | |||
CtClass clazz; | |||
switch (type) { | |||
case CLASS : | |||
clazz = lookupClassByJvmName(classname); | |||
if (dim > 0) | |||
cname = clazz.getName(); | |||
else | |||
return clazz; | |||
break; | |||
case BOOLEAN : | |||
cname = "boolean"; | |||
break; | |||
case CHAR : | |||
cname = "char"; | |||
break; | |||
case BYTE : | |||
cname = "byte"; | |||
break; | |||
case SHORT : | |||
cname = "short"; | |||
break; | |||
case INT : | |||
cname = "int"; | |||
break; | |||
case LONG : | |||
cname = "long"; | |||
break; | |||
case FLOAT : | |||
cname = "float"; | |||
break; | |||
case DOUBLE : | |||
cname = "double"; | |||
break; | |||
case VOID : | |||
cname = "void"; | |||
break; | |||
default : | |||
fatal(); | |||
} | |||
while (dim-- > 0) | |||
cname += "[]"; | |||
return lookupClass(cname); | |||
} | |||
/** | |||
* @param name a qualified class name. e.g. java.lang.String | |||
*/ | |||
public CtClass lookupClass(String name) throws CompileError { | |||
try { | |||
return classPool.get(name); | |||
} | |||
catch (NotFoundException e) {} | |||
try { | |||
if (name.indexOf('.') < 0) | |||
return classPool.get("java.lang." + name); | |||
} | |||
catch (NotFoundException e) {} | |||
throw new CompileError("no such class: " + name); | |||
} | |||
/* Converts a class name into a JVM-internal representation. | |||
* | |||
* It may also expand a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
public String resolveClassName(ASTList name) throws CompileError { | |||
if (name == null) | |||
return null; | |||
else | |||
return javaToJvmName(lookupClassByName(name).getName()); | |||
} | |||
/* Expands a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
public String resolveJvmClassName(String jvmName) throws CompileError { | |||
if (jvmName == null) | |||
return null; | |||
else | |||
return javaToJvmName(lookupClassByJvmName(jvmName).getName()); | |||
} | |||
public static CtClass getSuperclass(CtClass c) throws CompileError { | |||
try { | |||
return c.getSuperclass(); | |||
} | |||
catch (NotFoundException e) { | |||
throw new CompileError("cannot find the super class of " | |||
+ c.getName()); | |||
} | |||
} | |||
public static String javaToJvmName(String classname) { | |||
return classname.replace('.', '/'); | |||
} | |||
public static String jvmToJavaName(String classname) { | |||
return classname.replace('/', '.'); | |||
} | |||
public static int jvmTypeNameToExprType(char type) { | |||
switch(type) { | |||
case 'Z' : | |||
return BOOLEAN; | |||
case 'B' : | |||
return BYTE; | |||
case 'C' : | |||
return CHAR; | |||
case 'S' : | |||
return SHORT; | |||
case 'I' : | |||
return INT; | |||
case 'J' : | |||
return LONG; | |||
case 'F' : | |||
return FLOAT; | |||
case 'D' : | |||
return DOUBLE; | |||
case 'V' : | |||
return VOID; | |||
default : | |||
return CLASS; | |||
} | |||
} | |||
public static int descToType(char c) throws CompileError { | |||
switch (c) { | |||
case 'Z' : | |||
return BOOLEAN; | |||
case 'C' : | |||
return CHAR; | |||
case 'B' : | |||
return BYTE; | |||
case 'S' : | |||
return SHORT; | |||
case 'I' : | |||
return INT; | |||
case 'J' : | |||
return LONG; | |||
case 'F' : | |||
return FLOAT; | |||
case 'D' : | |||
return DOUBLE; | |||
case 'V' : | |||
return VOID; | |||
case 'L' : | |||
case '[' : | |||
return CLASS; | |||
default : | |||
fatal(); | |||
return VOID; | |||
} | |||
} | |||
public static int getModifiers(ASTList mods) { | |||
int m = 0; | |||
while (mods != null) { | |||
Keyword k = (Keyword)mods.head(); | |||
mods = mods.tail(); | |||
switch (k.get()) { | |||
case STATIC : | |||
m |= Modifier.STATIC; | |||
break; | |||
case FINAL : | |||
m |= Modifier.FINAL; | |||
break; | |||
case SYNCHRONIZED : | |||
m |= Modifier.SYNCHRONIZED; | |||
break; | |||
case ABSTRACT : | |||
m |= Modifier.ABSTRACT; | |||
break; | |||
case PUBLIC : | |||
m |= Modifier.PUBLIC; | |||
break; | |||
case PROTECTED : | |||
m |= Modifier.PROTECTED; | |||
break; | |||
case PRIVATE : | |||
m |= Modifier.PRIVATE; | |||
break; | |||
case VOLATILE : | |||
m |= Modifier.VOLATILE; | |||
break; | |||
case TRANSIENT : | |||
m |= Modifier.TRANSIENT; | |||
break; | |||
case STRICT : | |||
m |= Modifier.STRICT; | |||
break; | |||
} | |||
} | |||
return m; | |||
} | |||
} |
@@ -852,7 +852,7 @@ public final class Parser implements TokenId { | |||
case '!' : | |||
case '~' : | |||
t = lex.get(); | |||
return new Expr(t, parseUnaryExpr(tbl)); | |||
return Expr.make(t, parseUnaryExpr(tbl)); | |||
case '(' : | |||
return parseCast(tbl); | |||
default : | |||
@@ -1051,7 +1051,7 @@ public final class Parser implements TokenId { | |||
throw new SyntaxError(lex); | |||
} | |||
return Expr.make(CALL, expr, parseArgumentList(tbl)); | |||
return CallExpr.makeCall(expr, parseArgumentList(tbl)); | |||
} | |||
private String toClassName(ASTree name) |
@@ -25,6 +25,6 @@ import javassist.compiler.ast.ASTList; | |||
* @see javassist.compiler.JvstCodeGen#atMethodCall(Expr) | |||
*/ | |||
public interface ProceedHandler { | |||
void doit(JvstCodeGen gen, Bytecode b, ASTList args) | |||
throws CompileError; | |||
void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError; | |||
void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError; | |||
} |
@@ -0,0 +1,755 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.compiler; | |||
import javassist.CtClass; | |||
import javassist.CtField; | |||
import javassist.ClassPool; | |||
import javassist.NotFoundException; | |||
import javassist.compiler.ast.*; | |||
import javassist.bytecode.*; | |||
public class TypeChecker extends Visitor implements Opcode, TokenId { | |||
static final String javaLangObject = "java.lang.Object"; | |||
static final String jvmJavaLangObject = "java/lang/Object"; | |||
static final String jvmJavaLangString = "java/lang/String"; | |||
/* The following fields are used by atXXX() methods | |||
* for returning the type of the compiled expression. | |||
*/ | |||
protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... | |||
protected int arrayDim; | |||
protected String className; // JVM-internal representation | |||
protected MemberResolver resolver; | |||
protected CtClass thisClass; | |||
protected MethodInfo thisMethod; | |||
public TypeChecker(CtClass cc, ClassPool cp) { | |||
resolver = new MemberResolver(cp); | |||
thisClass = cc; | |||
thisMethod = null; | |||
} | |||
/** | |||
* Records the currently compiled method. | |||
*/ | |||
public void setThisMethod(MethodInfo m) { | |||
thisMethod = m; | |||
} | |||
protected static void fatal() throws CompileError { | |||
throw new CompileError("fatal"); | |||
} | |||
/** | |||
* Returns the JVM-internal representation of this class name. | |||
*/ | |||
protected String getThisName() { | |||
return MemberResolver.javaToJvmName(thisClass.getName()); | |||
} | |||
/** | |||
* Returns the JVM-internal representation of this super class name. | |||
*/ | |||
protected String getSuperName() throws CompileError { | |||
return MemberResolver.javaToJvmName( | |||
MemberResolver.getSuperclass(thisClass).getName()); | |||
} | |||
/* Converts a class name into a JVM-internal representation. | |||
* | |||
* It may also expand a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
protected String resolveClassName(ASTList name) throws CompileError { | |||
return resolver.resolveClassName(name); | |||
} | |||
/* Expands a simple class name to java.lang.*. | |||
* For example, this converts Object into java/lang/Object. | |||
*/ | |||
protected String resolveClassName(String jvmName) throws CompileError { | |||
return resolver.resolveJvmClassName(jvmName); | |||
} | |||
public void atNewExpr(NewExpr expr) throws CompileError { | |||
if (expr.isArray()) | |||
atNewArrayExpr(expr); | |||
else { | |||
CtClass clazz = resolver.lookupClassByName(expr.getClassName()); | |||
String cname = clazz.getName(); | |||
ASTList args = expr.getArguments(); | |||
atMethodCallCore(clazz, MethodInfo.nameInit, args); | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = MemberResolver.javaToJvmName(cname); | |||
} | |||
} | |||
public void atNewArrayExpr(NewExpr expr) throws CompileError { | |||
int type = expr.getArrayType(); | |||
ASTList size = expr.getArraySize(); | |||
ASTList classname = expr.getClassName(); | |||
if (size.length() > 1) | |||
atMultiNewArray(type, classname, size); | |||
else { | |||
size.head().accept(this); | |||
exprType = type; | |||
arrayDim = 1; | |||
if (type == CLASS) | |||
className = resolveClassName(classname); | |||
else | |||
className = null; | |||
} | |||
} | |||
protected void atMultiNewArray(int type, ASTList classname, ASTList size) | |||
throws CompileError | |||
{ | |||
int count, dim; | |||
dim = size.length(); | |||
for (count = 0; size != null; size = size.tail()) { | |||
ASTree s = size.head(); | |||
if (s == null) | |||
break; // int[][][] a = new int[3][4][]; | |||
++count; | |||
s.accept(this); | |||
} | |||
exprType = type; | |||
arrayDim = dim; | |||
if (type == CLASS) | |||
className = resolveClassName(classname); | |||
else | |||
className = null; | |||
} | |||
public void atAssignExpr(AssignExpr expr) throws CompileError { | |||
// =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= | |||
int op = expr.getOperator(); | |||
ASTree left = expr.oprand1(); | |||
ASTree right = expr.oprand2(); | |||
if (left instanceof Variable) | |||
atVariableAssign(expr, op, (Variable)left, | |||
((Variable)left).getDeclarator(), | |||
right); | |||
else { | |||
if (left instanceof Expr) { | |||
Expr e = (Expr)left; | |||
if (e.getOperator() == ARRAY) { | |||
atArrayAssign(expr, op, (Expr)left, right); | |||
return; | |||
} | |||
} | |||
atFieldAssign(expr, op, left, right); | |||
} | |||
} | |||
/* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. | |||
* | |||
* expr and var can be null. | |||
*/ | |||
private void atVariableAssign(Expr expr, int op, Variable var, | |||
Declarator d, ASTree right) | |||
throws CompileError | |||
{ | |||
int varType = d.getType(); | |||
int varArray = d.getArrayDim(); | |||
String varClass = d.getClassName(); | |||
if (op != '=') | |||
atVariable(var); | |||
right.accept(this); | |||
exprType = varType; | |||
arrayDim = varArray; | |||
className = varClass; | |||
} | |||
private void atArrayAssign(Expr expr, int op, Expr array, | |||
ASTree right) throws CompileError | |||
{ | |||
atArrayRead(array.oprand1(), array.oprand2()); | |||
int aType = exprType; | |||
int aDim = arrayDim; | |||
String cname = className; | |||
right.accept(this); | |||
exprType = aType; | |||
arrayDim = aDim; | |||
className = cname; | |||
} | |||
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) | |||
throws CompileError | |||
{ | |||
CtField f = fieldAccess(left); | |||
atFieldRead(f); | |||
int fType = exprType; | |||
int fDim = arrayDim; | |||
String cname = className; | |||
right.accept(this); | |||
exprType = fType; | |||
arrayDim = fDim; | |||
className = cname; | |||
} | |||
public void atCondExpr(CondExpr expr) throws CompileError { | |||
booleanExpr(expr.condExpr()); | |||
expr.thenExpr().accept(this); | |||
int type1 = exprType; | |||
int dim1 = arrayDim; | |||
String cname1 = className; | |||
expr.elseExpr().accept(this); | |||
if (dim1 == 0 && dim1 == arrayDim) | |||
if (CodeGen.rightIsStrong(type1, exprType)) | |||
expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); | |||
else if (CodeGen.rightIsStrong(exprType, type1)) { | |||
expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); | |||
exprType = type1; | |||
} | |||
} | |||
public void atBinExpr(BinExpr expr) throws CompileError { | |||
int token = expr.getOperator(); | |||
int k = CodeGen.lookupBinOp(token); | |||
if (k >= 0) { | |||
/* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> | |||
*/ | |||
if (token == '+') { | |||
Expr e = atPlusExpr(expr); | |||
if (e != null) { | |||
/* String concatenation has been translated into | |||
* an expression using StringBuffer. | |||
*/ | |||
e = CallExpr.makeCall(Expr.make('.', e, | |||
new Member("toString")), null); | |||
expr.setLeft(e); | |||
expr.setOprand2(null); // <---- look at this! | |||
className = jvmJavaLangString; | |||
} | |||
} | |||
else { | |||
expr.oprand1().accept(this); | |||
int type1 = exprType; | |||
expr.oprand2().accept(this); | |||
computeBinExprType(expr, token, type1); | |||
} | |||
} | |||
else { | |||
/* equation: &&, ||, ==, !=, <=, >=, <, > | |||
*/ | |||
booleanExpr(expr); | |||
} | |||
} | |||
// expr must be a + expression. | |||
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; | |||
return null; | |||
} | |||
if (isPlusExpr(left)) { | |||
Expr newExpr = atPlusExpr((BinExpr)left); | |||
if (newExpr != null) { | |||
right.accept(this); | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = "java/lang/StringBuffer"; | |||
return makeAppendCall(newExpr, right); | |||
} | |||
} | |||
else | |||
left.accept(this); | |||
int type1 = exprType; | |||
int dim1 = arrayDim; | |||
String cname = className; | |||
right.accept(this); | |||
if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) | |||
|| (exprType == CLASS && arrayDim == 0 | |||
&& jvmJavaLangString.equals(className))) { | |||
ASTList sbufClass = ASTList.make(new Symbol("java"), | |||
new Symbol("lang"), new Symbol("StringBuffer")); | |||
ASTree e = new NewExpr(sbufClass, null); | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = "java/lang/StringBuffer"; | |||
return makeAppendCall(makeAppendCall(e, left), right); | |||
} | |||
else { | |||
computeBinExprType(expr, '+', type1); | |||
return null; | |||
} | |||
} | |||
private static boolean isPlusExpr(ASTree expr) { | |||
if (expr instanceof BinExpr) { | |||
BinExpr bexpr = (BinExpr)expr; | |||
int token = bexpr.getOperator(); | |||
return token == '+'; | |||
} | |||
return false; | |||
} | |||
private static Expr makeAppendCall(ASTree target, ASTree arg) { | |||
return CallExpr.makeCall(Expr.make('.', target, new Member("append")), | |||
new ASTList(arg)); | |||
} | |||
private void computeBinExprType(BinExpr expr, int token, int type1) | |||
throws CompileError | |||
{ | |||
// arrayDim should be 0. | |||
int type2 = exprType; | |||
if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) | |||
exprType = type1; | |||
else | |||
insertCast(expr, type1, type2); | |||
if (CodeGen.isP_INT(exprType)) | |||
exprType = INT; // type1 may be BYTE, ... | |||
} | |||
private void booleanExpr(ASTree expr) | |||
throws CompileError | |||
{ | |||
int op = CodeGen.getCompOperator(expr); | |||
if (op == EQ) { // ==, !=, ... | |||
BinExpr bexpr = (BinExpr)expr; | |||
bexpr.oprand1().accept(this); | |||
int type1 = exprType; | |||
int dim1 = arrayDim; | |||
bexpr.oprand2().accept(this); | |||
if (dim1 == 0 && arrayDim == 0) | |||
insertCast(bexpr, type1, exprType); | |||
} | |||
else if (op == '!') | |||
((Expr)expr).oprand1().accept(this); | |||
else if (op == ANDAND || op == OROR) { | |||
BinExpr bexpr = (BinExpr)expr; | |||
bexpr.oprand1().accept(this); | |||
bexpr.oprand2().accept(this); | |||
} | |||
else // others | |||
expr.accept(this); | |||
exprType = BOOLEAN; | |||
arrayDim = 0; | |||
} | |||
private void insertCast(BinExpr expr, int type1, int type2) | |||
throws CompileError | |||
{ | |||
if (CodeGen.rightIsStrong(type1, type2)) | |||
expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); | |||
else | |||
exprType = type1; | |||
} | |||
public void atCastExpr(CastExpr expr) throws CompileError { | |||
String cname = resolveClassName(expr.getClassName()); | |||
expr.getOprand().accept(this); | |||
exprType = expr.getType(); | |||
arrayDim = expr.getArrayDim(); | |||
className = cname; | |||
} | |||
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { | |||
expr.getOprand().accept(this); | |||
exprType = BOOLEAN; | |||
arrayDim = 0; | |||
} | |||
public void atExpr(Expr expr) throws CompileError { | |||
// array access, member access, | |||
// (unary) +, (unary) -, ++, --, !, ~ | |||
int token = expr.getOperator(); | |||
ASTree oprand = expr.oprand1(); | |||
if (token == '.') | |||
if (((Symbol)expr.oprand2()).get().equals("length")) | |||
atArrayLength(expr); | |||
else | |||
atFieldRead(expr); | |||
else if (token == MEMBER) { // field read | |||
if (!atClassObject(expr)) // .class | |||
atFieldRead(expr); | |||
} | |||
else if (token == ARRAY) | |||
atArrayRead(oprand, expr.oprand2()); | |||
else if (token == PLUSPLUS || token == MINUSMINUS) | |||
atPlusPlus(token, oprand, expr); | |||
else if (token == '!') | |||
booleanExpr(expr); | |||
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, ... | |||
} | |||
} | |||
public void atCallExpr(CallExpr expr) throws CompileError { | |||
String mname = null; | |||
CtClass targetClass = null; | |||
ASTree method = expr.oprand1(); | |||
ASTList args = (ASTList)expr.oprand2(); | |||
if (method instanceof Member) { | |||
mname = ((Member)method).get(); | |||
targetClass = thisClass; | |||
} | |||
else if (method instanceof Keyword) { // constructor | |||
mname = MethodInfo.nameInit; // <init> | |||
if (((Keyword)method).get() == SUPER) | |||
targetClass = MemberResolver.getSuperclass(thisClass); | |||
else | |||
targetClass = thisClass; | |||
} | |||
else if (method instanceof Expr) { | |||
Expr e = (Expr)method; | |||
mname = ((Symbol)e.oprand2()).get(); | |||
int op = e.getOperator(); | |||
if (op == MEMBER) // static method | |||
targetClass | |||
= resolver.lookupClass(((Symbol)e.oprand1()).get()); | |||
else if (op == '.') { | |||
ASTree target = e.oprand1(); | |||
try { | |||
target.accept(this); | |||
} | |||
catch (NoFieldException nfe) { | |||
if (nfe.getExpr() != target) | |||
throw nfe; | |||
// it should be a static method. | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = nfe.getField(); // JVM-internal | |||
} | |||
if (arrayDim > 0) | |||
targetClass = resolver.lookupClass(javaLangObject); | |||
else if (exprType == CLASS /* && arrayDim == 0 */) | |||
targetClass = resolver.lookupClassByJvmName(className); | |||
else | |||
badMethod(); | |||
} | |||
else | |||
badMethod(); | |||
} | |||
else | |||
fatal(); | |||
MemberResolver.Method minfo | |||
= atMethodCallCore(targetClass, mname, args); | |||
expr.setMethod(minfo); | |||
} | |||
private static void badMethod() throws CompileError { | |||
throw new CompileError("bad method"); | |||
} | |||
/** | |||
* @return a pair of the class declaring the invoked method | |||
* and the MethodInfo of that method. Never null. | |||
*/ | |||
public MemberResolver.Method atMethodCallCore(CtClass targetClass, | |||
String mname, ASTList args) | |||
throws CompileError | |||
{ | |||
int nargs = getMethodArgsLength(args); | |||
int[] types = new int[nargs]; | |||
int[] dims = new int[nargs]; | |||
String[] cnames = new String[nargs]; | |||
atMethodArgs(args, types, dims, cnames); | |||
MemberResolver.Method found | |||
= resolver.lookupMethod(targetClass, thisMethod, mname, | |||
types, dims, cnames, false); | |||
if (found == null) { | |||
String msg; | |||
if (mname.equals(MethodInfo.nameInit)) | |||
msg = "constructor not found"; | |||
else | |||
msg = "Method " + mname + " not found in " | |||
+ targetClass.getName(); | |||
throw new CompileError(msg); | |||
} | |||
String desc = found.info.getDescriptor(); | |||
setReturnType(desc); | |||
return found; | |||
} | |||
public int getMethodArgsLength(ASTList args) { | |||
return ASTList.length(args); | |||
} | |||
public void atMethodArgs(ASTList args, int[] types, int[] dims, | |||
String[] cnames) throws CompileError { | |||
int i = 0; | |||
while (args != null) { | |||
ASTree a = args.head(); | |||
a.accept(this); | |||
types[i] = exprType; | |||
dims[i] = arrayDim; | |||
cnames[i] = className; | |||
++i; | |||
args = args.tail(); | |||
} | |||
} | |||
void setReturnType(String desc) throws CompileError { | |||
int i = desc.indexOf(')'); | |||
if (i < 0) | |||
badMethod(); | |||
char c = desc.charAt(++i); | |||
int dim = 0; | |||
while (c == '[') { | |||
++dim; | |||
c = desc.charAt(++i); | |||
} | |||
arrayDim = dim; | |||
if (c == 'L') { | |||
int j = desc.indexOf(';', i + 1); | |||
if (j < 0) | |||
badMethod(); | |||
exprType = CLASS; | |||
className = desc.substring(i + 1, j); | |||
} | |||
else { | |||
exprType = MemberResolver.descToType(c); | |||
className = null; | |||
} | |||
} | |||
private void atFieldRead(ASTree expr) throws CompileError { | |||
atFieldRead(fieldAccess(expr)); | |||
} | |||
private void atFieldRead(CtField f) throws CompileError { | |||
FieldInfo finfo = f.getFieldInfo2(); | |||
String type = finfo.getDescriptor(); | |||
int i = 0; | |||
int dim = 0; | |||
char c = type.charAt(i); | |||
while (c == '[') { | |||
++dim; | |||
c = type.charAt(++i); | |||
} | |||
arrayDim = dim; | |||
exprType = MemberResolver.descToType(c); | |||
if (c == 'L') | |||
className = type.substring(i + 1, type.indexOf(';', i + 1)); | |||
else | |||
className = null; | |||
} | |||
protected CtField fieldAccess(ASTree expr) throws CompileError { | |||
if (expr instanceof Member) { | |||
String name = ((Member)expr).get(); | |||
try { | |||
return thisClass.getField(name); | |||
} | |||
catch (NotFoundException e) { | |||
// EXPR might be part of a static member access? | |||
throw new NoFieldException(name, expr); | |||
} | |||
} | |||
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()); | |||
else if (op == '.') | |||
try { | |||
e.oprand1().accept(this); | |||
if (exprType == CLASS && arrayDim == 0) | |||
return resolver.lookupFieldByJvmName(className, | |||
(Symbol)e.oprand2()); | |||
} | |||
catch (NoFieldException nfe) { | |||
if (nfe.getExpr() != e.oprand1()) | |||
throw nfe; | |||
Symbol fname = (Symbol)e.oprand2(); | |||
// it should be a static field. | |||
try { | |||
return resolver.lookupFieldByJvmName(nfe.getField(), | |||
fname); | |||
} | |||
catch (CompileError ce) { | |||
// EXPR might be part of a qualified class name. | |||
throw new NoFieldException(nfe.getField() + "/" | |||
+ fname.get(), expr); | |||
} | |||
} | |||
} | |||
throw new CompileError("bad filed access"); | |||
} | |||
public boolean atClassObject(Expr expr) throws CompileError { | |||
if (!((Symbol)expr.oprand2()).get().equals("class")) | |||
return false; | |||
if (resolveClassName((ASTList)expr.oprand1()) == null) | |||
return false; | |||
return true; | |||
} | |||
public void atArrayLength(Expr expr) throws CompileError { | |||
expr.oprand1().accept(this); | |||
exprType = INT; | |||
arrayDim = 0; | |||
} | |||
public void atArrayRead(ASTree array, ASTree index) | |||
throws CompileError | |||
{ | |||
array.accept(this); | |||
int type = exprType; | |||
int dim = arrayDim; | |||
String cname = className; | |||
index.accept(this); | |||
exprType = type; | |||
arrayDim = dim - 1; | |||
className = cname; | |||
} | |||
private void atPlusPlus(int token, ASTree oprand, Expr expr) | |||
throws CompileError | |||
{ | |||
boolean isPost = oprand == null; // ++i or i++? | |||
if (isPost) | |||
oprand = expr.oprand2(); | |||
if (oprand instanceof Variable) { | |||
Declarator d = ((Variable)oprand).getDeclarator(); | |||
exprType = d.getType(); | |||
arrayDim = d.getArrayDim(); | |||
} | |||
else { | |||
if (oprand instanceof Expr) { | |||
Expr e = (Expr)oprand; | |||
if (e.getOperator() == ARRAY) { | |||
atArrayRead(expr.oprand1(), expr.oprand2()); | |||
// arrayDim should be 0. | |||
int t = exprType; | |||
if (t == INT || t == BYTE || t == CHAR || t == SHORT) | |||
exprType = INT; | |||
return; | |||
} | |||
} | |||
atFieldPlusPlus(oprand); | |||
} | |||
} | |||
protected void atFieldPlusPlus(ASTree oprand) throws CompileError | |||
{ | |||
CtField f = fieldAccess(oprand); | |||
atFieldRead(f); | |||
int t = exprType; | |||
if (t == INT || t == BYTE || t == CHAR || t == SHORT) | |||
exprType = INT; | |||
} | |||
public void atMember(Member mem) throws CompileError { | |||
atFieldRead(mem); | |||
} | |||
public void atVariable(Variable v) throws CompileError { | |||
Declarator d = v.getDeclarator(); | |||
exprType = d.getType(); | |||
arrayDim = d.getArrayDim(); | |||
className = d.getClassName(); | |||
} | |||
public void atKeyword(Keyword k) throws CompileError { | |||
arrayDim = 0; | |||
int token = k.get(); | |||
switch (token) { | |||
case TRUE : | |||
case FALSE : | |||
exprType = BOOLEAN; | |||
break; | |||
case NULL : | |||
exprType = NULL; | |||
break; | |||
case THIS : | |||
case SUPER : | |||
exprType = CLASS; | |||
if (token == THIS) | |||
className = getThisName(); | |||
else | |||
className = getSuperName(); | |||
break; | |||
default : | |||
fatal(); | |||
} | |||
} | |||
public void atStringL(StringL s) throws CompileError { | |||
exprType = CLASS; | |||
arrayDim = 0; | |||
className = jvmJavaLangString; | |||
} | |||
public void atIntConst(IntConst i) throws CompileError { | |||
arrayDim = 0; | |||
int type = i.getType(); | |||
if (type == IntConstant || type == CharConstant) | |||
exprType = (type == IntConstant ? INT : CHAR); | |||
else | |||
exprType = LONG; | |||
} | |||
public void atDoubleConst(DoubleConst d) throws CompileError { | |||
arrayDim = 0; | |||
if (d.getType() == DoubleConstant) | |||
exprType = DOUBLE; | |||
else | |||
exprType = FLOAT; | |||
} | |||
} |
@@ -25,7 +25,7 @@ public class AssignExpr extends Expr { | |||
* =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>= | |||
*/ | |||
public AssignExpr(int op, ASTree _head, ASTList _tail) { | |||
private AssignExpr(int op, ASTree _head, ASTList _tail) { | |||
super(op, _head, _tail); | |||
} | |||
@@ -19,6 +19,9 @@ import javassist.compiler.CompileError; | |||
/** | |||
* Binary expression. | |||
* | |||
* <p>If the operator is +, the right node might be null. | |||
* See TypeChecker.atBinExpr(). | |||
*/ | |||
public class BinExpr extends Expr { | |||
/* operator must be either of: | |||
@@ -26,7 +29,7 @@ public class BinExpr extends Expr { | |||
* <<, >>, >>>, +, -, *, /, % | |||
*/ | |||
public BinExpr(int op, ASTree _head, ASTList _tail) { | |||
private BinExpr(int op, ASTree _head, ASTList _tail) { | |||
super(op, _head, _tail); | |||
} | |||
@@ -0,0 +1,46 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.compiler.ast; | |||
import javassist.compiler.CompileError; | |||
import javassist.compiler.TokenId; | |||
import javassist.compiler.MemberResolver; | |||
/** | |||
* Method call expression. | |||
*/ | |||
public class CallExpr extends Expr { | |||
private MemberResolver.Method method; // cached result of lookupMethod() | |||
private CallExpr(ASTree _head, ASTList _tail) { | |||
super(TokenId.CALL, _head, _tail); | |||
method = null; | |||
} | |||
public void setMethod(MemberResolver.Method m) { | |||
method = m; | |||
} | |||
public MemberResolver.Method getMethod() { | |||
return method; | |||
} | |||
public static CallExpr makeCall(ASTree target, ASTree args) { | |||
return new CallExpr(target, new ASTList(args)); | |||
} | |||
public void accept(Visitor v) throws CompileError { v.atCallExpr(this); } | |||
} |
@@ -29,8 +29,12 @@ public class CondExpr extends ASTList { | |||
public ASTree thenExpr() { return tail().head(); } | |||
public void setThen(ASTree t) { tail().setHead(t); } | |||
public ASTree elseExpr() { return tail().tail().head(); } | |||
public void setElse(ASTree t) { tail().tail().setHead(t); } | |||
public String getTag() { return "?:"; } | |||
public void accept(Visitor v) throws CompileError { v.atCondExpr(this); } |
@@ -24,18 +24,18 @@ import javassist.compiler.CompileError; | |||
public class Expr extends ASTList implements TokenId { | |||
/* operator must be either of: | |||
* (unary) +, (unary) -, ++, --, !, ~, | |||
* CALL, ARRAY, . (dot), MEMBER (static member access). | |||
* ARRAY, . (dot), MEMBER (static member access). | |||
* Otherwise, the object should be an instance of a subclass. | |||
*/ | |||
protected int operatorId; | |||
public Expr(int op, ASTree _head, ASTList _tail) { | |||
Expr(int op, ASTree _head, ASTList _tail) { | |||
super(_head, _tail); | |||
operatorId = op; | |||
} | |||
public Expr(int op, ASTree _head) { | |||
Expr(int op, ASTree _head) { | |||
super(_head); | |||
operatorId = op; | |||
} | |||
@@ -44,12 +44,20 @@ public class Expr extends ASTList implements TokenId { | |||
return new Expr(op, oprand1, new ASTList(oprand2)); | |||
} | |||
public static Expr make(int op, ASTree oprand1) { | |||
return new Expr(op, oprand1); | |||
} | |||
public int getOperator() { return operatorId; } | |||
public ASTree oprand1() { return getLeft(); } | |||
public ASTree oprand2() { return getRight().getLeft(); } | |||
public void setOprand2(ASTree expr) { | |||
getRight().setLeft(expr); | |||
} | |||
public void accept(Visitor v) throws CompileError { v.atExpr(this); } | |||
public String getName() { |
@@ -35,6 +35,7 @@ public class Visitor { | |||
public void atCondExpr(CondExpr n) throws CompileError {} | |||
public void atBinExpr(BinExpr n) throws CompileError {} | |||
public void atExpr(Expr n) throws CompileError {} | |||
public void atCallExpr(CallExpr n) throws CompileError {} | |||
public void atCastExpr(CastExpr n) throws CompileError {} | |||
public void atInstanceOfExpr(InstanceOfExpr n) throws CompileError {} | |||
public void atNewExpr(NewExpr n) throws CompileError {} |
@@ -148,5 +148,12 @@ public class Cast extends Expr { | |||
bytecode.addIndex(index); | |||
gen.setType(retType); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | |||
c.setType(retType); | |||
} | |||
} | |||
} |
@@ -240,6 +240,12 @@ public class FieldAccess extends Expr { | |||
bytecode.growStack(stack); | |||
gen.setType(fieldType); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.setType(fieldType); | |||
} | |||
} | |||
/* void $proceed(<field type>) | |||
@@ -286,5 +292,13 @@ public class FieldAccess extends Expr { | |||
gen.setType(CtClass.voidType); | |||
gen.addNullIfVoid(); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | |||
c.setType(CtClass.voidType); | |||
c.addNullIfVoid(); | |||
} | |||
} | |||
} |
@@ -151,5 +151,12 @@ public class Instanceof extends Expr { | |||
bytecode.addIndex(index); | |||
gen.setType(CtClass.booleanType); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.atMethodArgs(args, new int[1], new int[1], new String[1]); | |||
c.setType(CtClass.booleanType); | |||
} | |||
} | |||
} |
@@ -205,9 +205,16 @@ public class NewExpr extends Expr { | |||
bytecode.addOpcode(NEW); | |||
bytecode.addIndex(newIndex); | |||
bytecode.addOpcode(DUP); | |||
gen.atMethodCall2(newType, MethodInfo.nameInit, | |||
args, false, true, -1); | |||
gen.atMethodCallCore(newType, MethodInfo.nameInit, args, | |||
false, true, -1, null); | |||
gen.setType(newType); | |||
} | |||
public void setReturnType(JvstTypeChecker c, ASTList args) | |||
throws CompileError | |||
{ | |||
c.atMethodCallCore(newType, MethodInfo.nameInit, args); | |||
c.setType(newType); | |||
} | |||
} | |||
} |