Browse Source

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
tags/rel_3_17_1_ga
chiba 20 years ago
parent
commit
cdca977159

+ 2
- 0
src/main/javassist/URLClassPath.java View File

@@ -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 {

+ 117
- 74
src/main/javassist/compiler/CodeGen.java View File

@@ -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());
}


+ 39
- 11
src/main/javassist/compiler/Javac.java View File

@@ -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);

+ 29
- 28
src/main/javassist/compiler/JvstCodeGen.java View File

@@ -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");

+ 282
- 0
src/main/javassist/compiler/JvstTypeChecker.java View File

@@ -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());
}
}
}

+ 54
- 394
src/main/javassist/compiler/MemberCodeGen.java View File

@@ -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);
}
}

+ 461
- 0
src/main/javassist/compiler/MemberResolver.java View File

@@ -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;
}
}

+ 2
- 2
src/main/javassist/compiler/Parser.java View File

@@ -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)

+ 2
- 2
src/main/javassist/compiler/ProceedHandler.java View File

@@ -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;
}

+ 755
- 0
src/main/javassist/compiler/TypeChecker.java View File

@@ -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;
}
}

+ 1
- 1
src/main/javassist/compiler/ast/AssignExpr.java View File

@@ -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);
}


+ 4
- 1
src/main/javassist/compiler/ast/BinExpr.java View File

@@ -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);
}


+ 46
- 0
src/main/javassist/compiler/ast/CallExpr.java View File

@@ -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); }
}

+ 4
- 0
src/main/javassist/compiler/ast/CondExpr.java View File

@@ -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); }

+ 11
- 3
src/main/javassist/compiler/ast/Expr.java View File

@@ -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() {

+ 1
- 0
src/main/javassist/compiler/ast/Visitor.java View File

@@ -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 {}

+ 7
- 0
src/main/javassist/expr/Cast.java View File

@@ -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);
}
}
}

+ 14
- 0
src/main/javassist/expr/FieldAccess.java View File

@@ -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();
}
}
}

+ 7
- 0
src/main/javassist/expr/Instanceof.java View File

@@ -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);
}
}
}

+ 9
- 2
src/main/javassist/expr/NewExpr.java View File

@@ -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);
}
}
}

Loading…
Cancel
Save