diff options
author | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-11-27 05:33:16 +0000 |
---|---|---|
committer | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2003-11-27 05:33:16 +0000 |
commit | cdca9771599b4c3337aaf42cf7460a27dc1719d5 (patch) | |
tree | 9617dfa2aa53d78c6bd2d3cf4428137d355faa76 /src/main/javassist | |
parent | ced4ae1f0e2130b36cb51faf6345d5b8e036585e (diff) | |
download | javassist-cdca9771599b4c3337aaf42cf7460a27dc1719d5.tar.gz javassist-cdca9771599b4c3337aaf42cf7460a27dc1719d5.zip |
I implemented a type checker for better code generation.
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@55 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'src/main/javassist')
20 files changed, 1847 insertions, 518 deletions
diff --git a/src/main/javassist/URLClassPath.java b/src/main/javassist/URLClassPath.java index 279b20f1..b455f381 100644 --- a/src/main/javassist/URLClassPath.java +++ b/src/main/javassist/URLClassPath.java @@ -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 { diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 6a0bd3dc..194c3fdb 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -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()); } diff --git a/src/main/javassist/compiler/Javac.java b/src/main/javassist/compiler/Javac.java index 44c3ff11..2436b4bf 100644 --- a/src/main/javassist/compiler/Javac.java +++ b/src/main/javassist/compiler/Javac.java @@ -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); diff --git a/src/main/javassist/compiler/JvstCodeGen.java b/src/main/javassist/compiler/JvstCodeGen.java index 5081ea1e..54e95a0a 100644 --- a/src/main/javassist/compiler/JvstCodeGen.java +++ b/src/main/javassist/compiler/JvstCodeGen.java @@ -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"); diff --git a/src/main/javassist/compiler/JvstTypeChecker.java b/src/main/javassist/compiler/JvstTypeChecker.java new file mode 100644 index 00000000..3b63180e --- /dev/null +++ b/src/main/javassist/compiler/JvstTypeChecker.java @@ -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()); + } + } +} diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 92e299f0..e6983d21 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -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); } } diff --git a/src/main/javassist/compiler/MemberResolver.java b/src/main/javassist/compiler/MemberResolver.java new file mode 100644 index 00000000..a716fa92 --- /dev/null +++ b/src/main/javassist/compiler/MemberResolver.java @@ -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; + } +} diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java index dcc14a24..46d17151 100644 --- a/src/main/javassist/compiler/Parser.java +++ b/src/main/javassist/compiler/Parser.java @@ -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) diff --git a/src/main/javassist/compiler/ProceedHandler.java b/src/main/javassist/compiler/ProceedHandler.java index 848e4525..765109a1 100644 --- a/src/main/javassist/compiler/ProceedHandler.java +++ b/src/main/javassist/compiler/ProceedHandler.java @@ -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; } diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java new file mode 100644 index 00000000..c8024b90 --- /dev/null +++ b/src/main/javassist/compiler/TypeChecker.java @@ -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; + } +} diff --git a/src/main/javassist/compiler/ast/AssignExpr.java b/src/main/javassist/compiler/ast/AssignExpr.java index 3f5b841e..77a109db 100644 --- a/src/main/javassist/compiler/ast/AssignExpr.java +++ b/src/main/javassist/compiler/ast/AssignExpr.java @@ -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); } diff --git a/src/main/javassist/compiler/ast/BinExpr.java b/src/main/javassist/compiler/ast/BinExpr.java index 3b8af793..0078d5c4 100644 --- a/src/main/javassist/compiler/ast/BinExpr.java +++ b/src/main/javassist/compiler/ast/BinExpr.java @@ -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); } diff --git a/src/main/javassist/compiler/ast/CallExpr.java b/src/main/javassist/compiler/ast/CallExpr.java new file mode 100644 index 00000000..7c8a63e6 --- /dev/null +++ b/src/main/javassist/compiler/ast/CallExpr.java @@ -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); } +} diff --git a/src/main/javassist/compiler/ast/CondExpr.java b/src/main/javassist/compiler/ast/CondExpr.java index 9cb821a5..9cfde8ad 100644 --- a/src/main/javassist/compiler/ast/CondExpr.java +++ b/src/main/javassist/compiler/ast/CondExpr.java @@ -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); } diff --git a/src/main/javassist/compiler/ast/Expr.java b/src/main/javassist/compiler/ast/Expr.java index aa7b06da..fb443448 100644 --- a/src/main/javassist/compiler/ast/Expr.java +++ b/src/main/javassist/compiler/ast/Expr.java @@ -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() { diff --git a/src/main/javassist/compiler/ast/Visitor.java b/src/main/javassist/compiler/ast/Visitor.java index 497eb17d..389cc688 100644 --- a/src/main/javassist/compiler/ast/Visitor.java +++ b/src/main/javassist/compiler/ast/Visitor.java @@ -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 {} diff --git a/src/main/javassist/expr/Cast.java b/src/main/javassist/expr/Cast.java index 5dcbbb41..6d65af52 100644 --- a/src/main/javassist/expr/Cast.java +++ b/src/main/javassist/expr/Cast.java @@ -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); + } } } diff --git a/src/main/javassist/expr/FieldAccess.java b/src/main/javassist/expr/FieldAccess.java index 0a9cfeb1..c10246c8 100644 --- a/src/main/javassist/expr/FieldAccess.java +++ b/src/main/javassist/expr/FieldAccess.java @@ -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(); + } } } diff --git a/src/main/javassist/expr/Instanceof.java b/src/main/javassist/expr/Instanceof.java index 9743c841..4053702e 100644 --- a/src/main/javassist/expr/Instanceof.java +++ b/src/main/javassist/expr/Instanceof.java @@ -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); + } } } diff --git a/src/main/javassist/expr/NewExpr.java b/src/main/javassist/expr/NewExpr.java index 423a5d72..62f25ad6 100644 --- a/src/main/javassist/expr/NewExpr.java +++ b/src/main/javassist/expr/NewExpr.java @@ -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); + } } } |