javassist.compiler.MemberCodeGen Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- 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,
* or the Apache License Version 2.0.
*
* 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.bytecode.*;
import javassist.compiler.ast.*;
import java.util.ArrayList;
/* Code generator methods depending on javassist.* classes.
*/
public class MemberCodeGen extends CodeGen {
protected MemberResolver resolver;
protected CtClass thisClass;
protected MethodInfo thisMethod;
protected boolean resultStatic;
public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
super(b);
resolver = new MemberResolver(cp);
thisClass = cc;
thisMethod = null;
}
/**
* Returns the major version of the class file
* targeted by this compilation.
*/
public int getMajorVersion() {
ClassFile cf = thisClass.getClassFile2();
if (cf == null)
return ClassFile.MAJOR_VERSION; // JDK 1.3
else
return cf.getMajorVersion();
}
/**
* Records the currently compiled method.
*/
public void setThisMethod(CtMethod m) {
thisMethod = m.getMethodInfo2();
if (typeChecker != null)
typeChecker.setThisMethod(thisMethod);
}
public CtClass getThisClass() { return thisClass; }
/**
* 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());
}
protected void insertDefaultSuperCall() throws CompileError {
bytecode.addAload(0);
bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
"", "()V");
}
static class JsrHook extends ReturnHook {
ArrayList jsrList;
CodeGen cgen;
int var;
JsrHook(CodeGen gen) {
super(gen);
jsrList = new ArrayList();
cgen = gen;
var = -1;
}
private int getVar(int size) {
if (var < 0) {
var = cgen.getMaxLocals();
cgen.incMaxLocals(size);
}
return var;
}
private void jsrJmp(Bytecode b) {
b.addOpcode(Opcode.GOTO);
jsrList.add(new int[] {b.currentPc(), var});
b.addIndex(0);
}
protected boolean doit(Bytecode b, int opcode) {
switch (opcode) {
case Opcode.RETURN :
jsrJmp(b);
break;
case ARETURN :
b.addAstore(getVar(1));
jsrJmp(b);
b.addAload(var);
break;
case IRETURN :
b.addIstore(getVar(1));
jsrJmp(b);
b.addIload(var);
break;
case LRETURN :
b.addLstore(getVar(2));
jsrJmp(b);
b.addLload(var);
break;
case DRETURN :
b.addDstore(getVar(2));
jsrJmp(b);
b.addDload(var);
break;
case FRETURN :
b.addFstore(getVar(1));
jsrJmp(b);
b.addFload(var);
break;
default :
throw new RuntimeException("fatal");
}
return false;
}
}
static class JsrHook2 extends ReturnHook {
int var;
int target;
JsrHook2(CodeGen gen, int[] retTarget) {
super(gen);
target = retTarget[0];
var = retTarget[1];
}
protected boolean doit(Bytecode b, int opcode) {
switch (opcode) {
case Opcode.RETURN :
break;
case ARETURN :
b.addAstore(var);
break;
case IRETURN :
b.addIstore(var);
break;
case LRETURN :
b.addLstore(var);
break;
case DRETURN :
b.addDstore(var);
break;
case FRETURN :
b.addFstore(var);
break;
default :
throw new RuntimeException("fatal");
}
b.addOpcode(Opcode.GOTO);
b.addIndex(target - b.currentPc() + 3);
return true;
}
}
protected void atTryStmnt(Stmnt st) throws CompileError {
Bytecode bc = bytecode;
Stmnt body = (Stmnt)st.getLeft();
if (body == null)
return;
ASTList catchList = (ASTList)st.getRight().getLeft();
Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
ArrayList gotoList = new ArrayList();
JsrHook jsrHook = null;
if (finallyBlock != null)
jsrHook = new JsrHook(this);
int start = bc.currentPc();
body.accept(this);
int end = bc.currentPc();
if (start == end)
throw new CompileError("empty try block");
boolean tryNotReturn = !hasReturned;
if (tryNotReturn) {
bc.addOpcode(Opcode.GOTO);
gotoList.add(new Integer(bc.currentPc()));
bc.addIndex(0); // correct later
}
int var = getMaxLocals();
incMaxLocals(1);
while (catchList != null) {
// catch clause
Pair p = (Pair)catchList.head();
catchList = catchList.tail();
Declarator decl = (Declarator)p.getLeft();
Stmnt block = (Stmnt)p.getRight();
decl.setLocalVar(var);
CtClass type = resolver.lookupClassByJvmName(decl.getClassName());
decl.setClassName(MemberResolver.javaToJvmName(type.getName()));
bc.addExceptionHandler(start, end, bc.currentPc(), type);
bc.growStack(1);
bc.addAstore(var);
hasReturned = false;
if (block != null)
block.accept(this);
if (!hasReturned) {
bc.addOpcode(Opcode.GOTO);
gotoList.add(new Integer(bc.currentPc()));
bc.addIndex(0); // correct later
tryNotReturn = true;
}
}
if (finallyBlock != null) {
jsrHook.remove(this);
// catch (any) clause
int pcAnyCatch = bc.currentPc();
bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
bc.growStack(1);
bc.addAstore(var);
hasReturned = false;
finallyBlock.accept(this);
if (!hasReturned) {
bc.addAload(var);
bc.addOpcode(ATHROW);
}
addFinally(jsrHook.jsrList, finallyBlock);
}
int pcEnd = bc.currentPc();
patchGoto(gotoList, pcEnd);
hasReturned = !tryNotReturn;
if (finallyBlock != null) {
if (tryNotReturn)
finallyBlock.accept(this);
}
}
/**
* Adds a finally clause for earch return statement.
*/
private void addFinally(ArrayList returnList, Stmnt finallyBlock)
throws CompileError
{
Bytecode bc = bytecode;
int n = returnList.size();
for (int i = 0; i < n; ++i) {
final int[] ret = (int[])returnList.get(i);
int pc = ret[0];
bc.write16bit(pc, bc.currentPc() - pc + 1);
ReturnHook hook = new JsrHook2(this, ret);
finallyBlock.accept(this);
hook.remove(this);
if (!hasReturned) {
bc.addOpcode(Opcode.GOTO);
bc.addIndex(pc + 3 - bc.currentPc());
}
}
}
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();
bytecode.addNew(cname);
bytecode.addOpcode(DUP);
atMethodCallCore(clazz, MethodInfo.nameInit, args,
false, true, -1, null);
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();
ArrayInit init = expr.getInitializer();
if (size.length() > 1) {
if (init != null)
throw new CompileError(
"sorry, multi-dimensional array initializer " +
"for new is not supported");
atMultiNewArray(type, classname, size);
return;
}
ASTree sizeExpr = size.head();
atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init);
}
private void atNewArrayExpr2(int type, ASTree sizeExpr,
String jvmClassname, ArrayInit init) throws CompileError {
if (init == null)
if (sizeExpr == null)
throw new CompileError("no array size");
else
sizeExpr.accept(this);
else
if (sizeExpr == null) {
int s = init.length();
bytecode.addIconst(s);
}
else
throw new CompileError("unnecessary array size specified for new");
String elementClass;
if (type == CLASS) {
elementClass = resolveClassName(jvmClassname);
bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass));
}
else {
elementClass = null;
int atype = 0;
switch (type) {
case BOOLEAN :
atype = T_BOOLEAN;
break;
case CHAR :
atype = T_CHAR;
break;
case FLOAT :
atype = T_FLOAT;
break;
case DOUBLE :
atype = T_DOUBLE;
break;
case BYTE :
atype = T_BYTE;
break;
case SHORT :
atype = T_SHORT;
break;
case INT :
atype = T_INT;
break;
case LONG :
atype = T_LONG;
break;
default :
badNewExpr();
break;
}
bytecode.addOpcode(NEWARRAY);
bytecode.add(atype);
}
if (init != null) {
int s = init.length();
ASTList list = init;
for (int i = 0; i < s; i++) {
bytecode.addOpcode(DUP);
bytecode.addIconst(i);
list.head().accept(this);
if (!isRefType(type))
atNumCastExpr(exprType, type);
bytecode.addOpcode(getArrayWriteOp(type, 0));
list = list.tail();
}
}
exprType = type;
arrayDim = 1;
className = elementClass;
}
private static void badNewExpr() throws CompileError {
throw new CompileError("bad new expression");
}
protected void atArrayVariableAssign(ArrayInit init, int varType,
int varArray, String varClass) throws CompileError {
atNewArrayExpr2(varType, null, varClass, init);
}
public void atArrayInit(ArrayInit init) throws CompileError {
throw new CompileError("array initializer is not supported");
}
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);
if (exprType != INT)
throw new CompileError("bad type for array size");
}
String desc;
exprType = type;
arrayDim = dim;
if (type == CLASS) {
className = resolveClassName(classname);
desc = toJvmArrayName(className, dim);
}
else
desc = toJvmTypeName(type, dim);
bytecode.addMultiNewarray(desc, count);
}
public void atCallExpr(CallExpr expr) throws CompileError {
String mname = null;
CtClass targetClass = null;
ASTree method = expr.oprand1();
ASTList args = (ASTList)expr.oprand2();
boolean isStatic = false;
boolean isSpecial = false;
int aload0pos = -1;
MemberResolver.Method cached = expr.getMethod();
if (method instanceof Member) {
mname = ((Member)method).get();
targetClass = thisClass;
if (inStaticMethod || (cached != null && cached.isStatic()))
isStatic = true; // should be static
else {
aload0pos = bytecode.currentPc();
bytecode.addAload(0); // this
}
}
else if (method instanceof Keyword) { // constructor
isSpecial = true;
mname = MethodInfo.nameInit; //
targetClass = thisClass;
if (inStaticMethod)
throw new CompileError("a constructor cannot be static");
else
bytecode.addAload(0); // this
if (((Keyword)method).get() == SUPER)
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
= resolver.lookupClass(((Symbol)e.oprand1()).get(), false);
isStatic = true;
}
else if (op == '.') {
ASTree target = e.oprand1();
if (target instanceof Keyword)
if (((Keyword)target).get() == SUPER)
isSpecial = true;
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
isStatic = true;
}
if (arrayDim > 0)
targetClass = resolver.lookupClass(javaLangObject, true);
else if (exprType == CLASS /* && arrayDim == 0 */)
targetClass = resolver.lookupClassByJvmName(className);
else
badMethod();
}
else
badMethod();
}
else
fatal();
atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
aload0pos, cached);
}
private static void badMethod() throws CompileError {
throw new CompileError("bad method");
}
/*
* atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
*
* @param targetClass the class at which method lookup starts.
* @param found not null if the method look has been already done.
*/
public void atMethodCallCore(CtClass targetClass, String mname,
ASTList args, boolean isStatic, boolean isSpecial,
int aload0pos, MemberResolver.Method found)
throws CompileError
{
int nargs = getMethodArgsLength(args);
int[] types = new int[nargs];
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();
// generate code for evaluating arguments.
atMethodArgs(args, types, dims, cnames);
// used by invokeinterface
int count = bytecode.getStackDepth() - stack + 1;
if (found == null)
found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
mname, types, dims, cnames);
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);
}
atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
aload0pos, count, found);
}
private void atMethodCallCore2(CtClass targetClass, String mname,
boolean isStatic, boolean isSpecial,
int aload0pos, int count,
MemberResolver.Method found)
throws CompileError
{
CtClass declClass = found.declaring;
MethodInfo minfo = found.info;
String desc = minfo.getDescriptor();
int acc = minfo.getAccessFlags();
if (mname.equals(MethodInfo.nameInit)) {
isSpecial = true;
if (declClass != targetClass)
throw new CompileError("no such constructor: " + targetClass.getName());
if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
desc = getAccessibleConstructor(desc, declClass, minfo);
bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
}
}
else if (AccessFlag.isPrivate(acc))
if (declClass == thisClass)
isSpecial = true;
else {
isSpecial = false;
isStatic = true;
String origDesc = desc;
if ((acc & AccessFlag.STATIC) == 0)
desc = Descriptor.insertParameter(declClass.getName(),
origDesc);
acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
mname = getAccessiblePrivate(mname, origDesc, desc,
minfo, declClass);
}
boolean popTarget = false;
if ((acc & AccessFlag.STATIC) != 0) {
if (!isStatic) {
/* this method is static but the target object is
on stack. It must be popped out. If aload0pos >= 0,
then the target object was pushed by aload_0. It is
overwritten by NOP.
*/
isStatic = true;
if (aload0pos >= 0)
bytecode.write(aload0pos, NOP);
else
popTarget = true;
}
bytecode.addInvokestatic(declClass, mname, desc);
}
else if (isSpecial) // if (isSpecial && notStatic(acc))
bytecode.addInvokespecial(declClass, mname, desc);
else {
if (!Modifier.isPublic(declClass.getModifiers())
|| declClass.isInterface() != targetClass.isInterface())
declClass = targetClass;
if (declClass.isInterface())
bytecode.addInvokeinterface(declClass, mname, desc, count);
else
if (isStatic)
throw new CompileError(mname + " is not static");
else
bytecode.addInvokevirtual(declClass, mname, desc);
}
setReturnType(desc, isStatic, popTarget);
}
/*
* Finds (or adds if necessary) a hidden accessor if the method
* is in an enclosing class.
*
* @param desc the descriptor of the method.
* @param declClass the class declaring the method.
*/
protected String getAccessiblePrivate(String methodName, String desc,
String newDesc, MethodInfo minfo,
CtClass declClass)
throws CompileError
{
if (isEnclosing(declClass, thisClass)) {
AccessorMaker maker = declClass.getAccessorMaker();
if (maker != null)
return maker.getMethodAccessor(methodName, desc, newDesc,
minfo);
}
throw new CompileError("Method " + methodName
+ " is private");
}
/*
* Finds (or adds if necessary) a hidden constructor if the given
* constructor is in an enclosing class.
*
* @param desc the descriptor of the constructor.
* @param declClass the class declaring the constructor.
* @param minfo the method info of the constructor.
* @return the descriptor of the hidden constructor.
*/
protected String getAccessibleConstructor(String desc, CtClass declClass,
MethodInfo minfo)
throws CompileError
{
if (isEnclosing(declClass, thisClass)) {
AccessorMaker maker = declClass.getAccessorMaker();
if (maker != null)
return maker.getConstructor(declClass, desc, minfo);
}
throw new CompileError("the called constructor is private in "
+ declClass.getName());
}
private boolean isEnclosing(CtClass outer, CtClass inner) {
try {
while (inner != null) {
inner = inner.getDeclaringClass();
if (inner == outer)
return true;
}
}
catch (NotFoundException e) {}
return false;
}
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, boolean isStatic, boolean popTarget)
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;
}
int etype = exprType;
if (isStatic) {
if (popTarget) {
if (is2word(etype, dim)) {
bytecode.addOpcode(DUP2_X1);
bytecode.addOpcode(POP2);
bytecode.addOpcode(POP);
}
else if (etype == VOID)
bytecode.addOpcode(POP);
else {
bytecode.addOpcode(SWAP);
bytecode.addOpcode(POP);
}
}
}
}
protected void atFieldAssign(Expr expr, int op, ASTree left,
ASTree right, boolean doDup) throws CompileError
{
CtField f = fieldAccess(left, false);
boolean is_static = resultStatic;
if (op != '=' && !is_static)
bytecode.addOpcode(DUP);
int fi;
if (op == '=') {
FieldInfo finfo = f.getFieldInfo2();
setFieldType(finfo);
AccessorMaker maker = isAccessibleField(f, finfo);
if (maker == null)
fi = addFieldrefInfo(f, finfo);
else
fi = 0;
}
else
fi = atFieldRead(f, is_static);
int fType = exprType;
int fDim = arrayDim;
String cname = className;
atAssignCore(expr, op, right, fType, fDim, cname);
boolean is2w = is2word(fType, fDim);
if (doDup) {
int dup_code;
if (is_static)
dup_code = (is2w ? DUP2 : DUP);
else
dup_code = (is2w ? DUP2_X1 : DUP_X1);
bytecode.addOpcode(dup_code);
}
atFieldAssignCore(f, is_static, fi, is2w);
exprType = fType;
arrayDim = fDim;
className = cname;
}
/* If fi == 0, the field must be a private field in an enclosing class.
*/
private void atFieldAssignCore(CtField f, boolean is_static, int fi,
boolean is2byte) throws CompileError {
if (fi != 0) {
if (is_static) {
bytecode.add(PUTSTATIC);
bytecode.growStack(is2byte ? -2 : -1);
}
else {
bytecode.add(PUTFIELD);
bytecode.growStack(is2byte ? -3 : -2);
}
bytecode.addIndex(fi);
}
else {
CtClass declClass = f.getDeclaringClass();
AccessorMaker maker = declClass.getAccessorMaker();
// make should be non null.
FieldInfo finfo = f.getFieldInfo2();
MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
bytecode.addInvokestatic(declClass, minfo.getName(),
minfo.getDescriptor());
}
}
/* overwritten in JvstCodeGen.
*/
public void atMember(Member mem) throws CompileError {
atFieldRead(mem);
}
protected void atFieldRead(ASTree expr) throws CompileError
{
CtField f = fieldAccess(expr, true);
if (f == null) {
atArrayLength(expr);
return;
}
boolean is_static = resultStatic;
ASTree cexpr = TypeChecker.getConstantFieldValue(f);
if (cexpr == null)
atFieldRead(f, is_static);
else {
cexpr.accept(this);
setFieldType(f.getFieldInfo2());
}
}
private void atArrayLength(ASTree expr) throws CompileError {
if (arrayDim == 0)
throw new CompileError(".length applied to a non array");
bytecode.addOpcode(ARRAYLENGTH);
exprType = INT;
arrayDim = 0;
}
/**
* Generates bytecode for reading a field value.
* It returns a fieldref_info index or zero if the field is a private
* one declared in an enclosing class.
*/
private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
FieldInfo finfo = f.getFieldInfo2();
boolean is2byte = setFieldType(finfo);
AccessorMaker maker = isAccessibleField(f, finfo);
if (maker != null) {
MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
minfo.getDescriptor());
return 0;
}
else {
int fi = addFieldrefInfo(f, finfo);
if (isStatic) {
bytecode.add(GETSTATIC);
bytecode.growStack(is2byte ? 2 : 1);
}
else {
bytecode.add(GETFIELD);
bytecode.growStack(is2byte ? 1 : 0);
}
bytecode.addIndex(fi);
return fi;
}
}
/**
* Returns null if the field is accessible. Otherwise, it throws
* an exception or it returns AccessorMaker if the field is a private
* one declared in an enclosing class.
*/
private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
throws CompileError
{
if (AccessFlag.isPrivate(finfo.getAccessFlags())
&& f.getDeclaringClass() != thisClass) {
CtClass declClass = f.getDeclaringClass();
if (isEnclosing(declClass, thisClass)) {
AccessorMaker maker = declClass.getAccessorMaker();
if (maker != null)
return maker;
else
throw new CompileError("fatal error. bug?");
}
else
throw new CompileError("Field " + f.getName() + " in "
+ declClass.getName() + " is private.");
}
return null; // accessible field
}
/**
* Sets exprType, arrayDim, and className.
*
* @return true if the field type is long or double.
*/
private boolean setFieldType(FieldInfo finfo) throws CompileError {
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;
boolean is2byte = dim == 0 && (c == 'J' || c == 'D');
return is2byte;
}
private int addFieldrefInfo(CtField f, FieldInfo finfo) {
ConstPool cp = bytecode.getConstPool();
String cname = f.getDeclaringClass().getName();
int ci = cp.addClassInfo(cname);
String name = finfo.getName();
String type = finfo.getDescriptor();
return cp.addFieldrefInfo(ci, name, type);
}
protected void atClassObject2(String cname) throws CompileError {
if (getMajorVersion() < ClassFile.JAVA_5)
super.atClassObject2(cname);
else
bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
}
protected void atFieldPlusPlus(int token, boolean isPost,
ASTree oprand, Expr expr, boolean doDup)
throws CompileError
{
CtField f = fieldAccess(oprand, false);
boolean is_static = resultStatic;
if (!is_static)
bytecode.addOpcode(DUP);
int fi = atFieldRead(f, is_static);
int t = exprType;
boolean is2w = is2word(t, arrayDim);
int dup_code;
if (is_static)
dup_code = (is2w ? DUP2 : DUP);
else
dup_code = (is2w ? DUP2_X1 : DUP_X1);
atPlusPlusCore(dup_code, doDup, token, isPost, expr);
atFieldAssignCore(f, is_static, fi, is2w);
}
/* This method also returns a value in resultStatic.
*
* @param acceptLength true if array length is acceptable
*/
protected CtField fieldAccess(ASTree expr, boolean acceptLength)
throws CompileError
{
if (expr instanceof Member) {
String name = ((Member)expr).get();
CtField f = null;
try {
f = thisClass.getField(name);
}
catch (NotFoundException e) {
// EXPR might be part of a static member access?
throw new NoFieldException(name, expr);
}
boolean is_static = Modifier.isStatic(f.getModifiers());
if (!is_static)
if (inStaticMethod)
throw new CompileError(
"not available in a static method: " + name);
else
bytecode.addAload(0); // this
resultStatic = is_static;
return f;
}
else if (expr instanceof Expr) {
Expr e = (Expr)expr;
int op = e.getOperator();
if (op == MEMBER) {
/* static member by # (extension by Javassist)
* For example, if int.class is parsed, the resulting tree
* is (# "java.lang.Integer" "TYPE").
*/
CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(),
(Symbol)e.oprand2());
resultStatic = true;
return f;
}
else if (op == '.') {
CtField f = null;
try {
e.oprand1().accept(this);
/* Don't call lookupFieldByJvmName2().
* The left operand of . is not a class name but
* a normal expression.
*/
if (exprType == CLASS && arrayDim == 0)
f = resolver.lookupFieldByJvmName(className,
(Symbol)e.oprand2());
else if (acceptLength && arrayDim > 0
&& ((Symbol)e.oprand2()).get().equals("length"))
return null; // expr is an array length.
else
badLvalue();
boolean is_static = Modifier.isStatic(f.getModifiers());
if (is_static)
bytecode.addOpcode(POP);
resultStatic = is_static;
return f;
}
catch (NoFieldException nfe) {
if (nfe.getExpr() != e.oprand1())
throw nfe;
/* EXPR should be a static field.
* If EXPR might be part of a qualified class name,
* lookupFieldByJvmName2() throws NoFieldException.
*/
Symbol fname = (Symbol)e.oprand2();
String cname = nfe.getField();
f = resolver.lookupFieldByJvmName2(cname, fname, expr);
resultStatic = true;
return f;
}
}
else
badLvalue();
}
else
badLvalue();
resultStatic = false;
return null; // never reach
}
private static void badLvalue() throws CompileError {
throw new CompileError("bad l-value");
}
public CtClass[] makeParamList(MethodDecl md) throws CompileError {
CtClass[] params;
ASTList plist = md.getParams();
if (plist == null)
params = new CtClass[0];
else {
int i = 0;
params = new CtClass[plist.length()];
while (plist != null) {
params[i++] = resolver.lookupClass((Declarator)plist.head());
plist = plist.tail();
}
}
return params;
}
public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
CtClass[] clist;
ASTList list = md.getThrows();
if (list == null)
return null;
else {
int i = 0;
clist = new CtClass[list.length()];
while (list != null) {
clist[i++] = resolver.lookupClassByName((ASTList)list.head());
list = list.tail();
}
return clist;
}
}
/* 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);
}
}