com.feilong.lib.javassist.compiler.MemberCodeGen Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
/*
* 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 com.feilong.lib.javassist.compiler;
import java.util.ArrayList;
import java.util.List;
import com.feilong.lib.javassist.ClassPool;
import com.feilong.lib.javassist.CtClass;
import com.feilong.lib.javassist.CtField;
import com.feilong.lib.javassist.CtMethod;
import com.feilong.lib.javassist.Modifier;
import com.feilong.lib.javassist.NotFoundException;
import com.feilong.lib.javassist.bytecode.AccessFlag;
import com.feilong.lib.javassist.bytecode.Bytecode;
import com.feilong.lib.javassist.bytecode.ClassFile;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.Descriptor;
import com.feilong.lib.javassist.bytecode.FieldInfo;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;
import com.feilong.lib.javassist.compiler.ast.ASTList;
import com.feilong.lib.javassist.compiler.ast.ASTree;
import com.feilong.lib.javassist.compiler.ast.ArrayInit;
import com.feilong.lib.javassist.compiler.ast.CallExpr;
import com.feilong.lib.javassist.compiler.ast.Declarator;
import com.feilong.lib.javassist.compiler.ast.Expr;
import com.feilong.lib.javassist.compiler.ast.Keyword;
import com.feilong.lib.javassist.compiler.ast.Member;
import com.feilong.lib.javassist.compiler.ast.MethodDecl;
import com.feilong.lib.javassist.compiler.ast.NewExpr;
import com.feilong.lib.javassist.compiler.ast.Pair;
import com.feilong.lib.javassist.compiler.ast.Stmnt;
import com.feilong.lib.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 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)){
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){
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);
}
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);
}
}