javassist.compiler.MemberCodeGen Maven / Gradle / Ivy
The newest version!
/*
* 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 java.util.ArrayList;
import java.util.List;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.compiler.ast.ASTList;
import javassist.compiler.ast.ASTree;
import javassist.compiler.ast.ArrayInit;
import javassist.compiler.ast.CallExpr;
import javassist.compiler.ast.Declarator;
import javassist.compiler.ast.Expr;
import javassist.compiler.ast.Keyword;
import javassist.compiler.ast.Member;
import javassist.compiler.ast.MethodDecl;
import javassist.compiler.ast.NewExpr;
import javassist.compiler.ast.Pair;
import javassist.compiler.ast.Stmnt;
import javassist.compiler.ast.Symbol;
/* 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
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.
*/
@Override
protected String getThisName() {
return MemberResolver.javaToJvmName(thisClass.getName());
}
/**
* Returns the JVM-internal representation of this super class name.
*/
@Override
protected String getSuperName() throws CompileError {
return MemberResolver.javaToJvmName(
MemberResolver.getSuperclass(thisClass).getName());
}
@Override
protected void insertDefaultSuperCall() throws CompileError {
bytecode.addAload(0);
bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
"", "()V");
}
static class JsrHook extends ReturnHook {
List 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);
}
@Override
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];
}
@Override
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;
}
}
@Override
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();
List 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(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(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(List returnList, Stmnt finallyBlock)
throws CompileError
{
Bytecode bc = bytecode;
for (final int[] ret:returnList) {
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());
}
}
}
@Override
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");
}
@Override
protected void atArrayVariableAssign(ArrayInit init, int varType,
int varArray, String varClass) throws CompileError {
atNewArrayExpr2(varType, null, varClass, init);
}
@Override
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);
}
@Override
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");
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();
String classFollowedByDotSuper = TypeChecker.isDotSuper(target);
if (classFollowedByDotSuper != null) {
isSpecial = true;
targetClass = MemberResolver.getSuperInterface(thisClass,
classFollowedByDotSuper);
if (inStaticMethod || (cached != null && cached.isStatic()))
isStatic = true; // should be static
else {
aload0pos = bytecode.currentPc();
bytecode.addAload(0); // this
}
}
else {
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;
}
@SuppressWarnings("unused")
int stack = bytecode.getStackDepth();
// generate code for evaluating arguments.
atMethodArgs(args, types, dims, cnames);
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, found);
}
private boolean isFromSameDeclaringClass(CtClass outer, CtClass inner) {
try {
while (outer != null) {
if (isEnclosing(outer, inner))
return true;
outer = outer.getDeclaringClass();
}
}
catch (NotFoundException e) {}
return false;
}
private void atMethodCallCore2(CtClass targetClass, String mname,
boolean isStatic, boolean isSpecial,
int aload0pos,
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)) {
if (declClass.getClassFile().getMajorVersion() < ClassFile.JAVA_11
|| !isFromSameDeclaringClass(declClass, thisClass)) {
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(targetClass, mname, desc);
else {
if (!Modifier.isPublic(declClass.getModifiers())
|| declClass.isInterface() != targetClass.isInterface())
declClass = targetClass;
if (declClass.isInterface()) {
int nargs = Descriptor.paramSize(desc) + 1;
bytecode.addInvokeinterface(declClass, mname, desc, nargs);
}
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);
}
}
}
}
@Override
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.
*/
@Override
public void atMember(Member mem) throws CompileError {
atFieldRead(mem);
}
@Override
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;
}
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;
}
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);
}
@Override
protected void atClassObject2(String cname) throws CompileError {
if (getMajorVersion() < ClassFile.JAVA_5)
super.atClassObject2(cname);
else
bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
}
@Override
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;
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.
*/
@Override
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.
*/
@Override
protected String resolveClassName(String jvmName) throws CompileError {
return resolver.resolveJvmClassName(jvmName);
}
}