Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.compiler;
     17 
     18 import javassist.*;
     19 import javassist.bytecode.*;
     20 import javassist.compiler.ast.*;
     21 
     22 import java.util.ArrayList;
     23 
     24 /* Code generator methods depending on javassist.* classes.
     25  */
     26 public class MemberCodeGen extends CodeGen {
     27     protected MemberResolver resolver;
     28     protected CtClass   thisClass;
     29     protected MethodInfo thisMethod;
     30 
     31     protected boolean resultStatic;
     32 
     33     public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
     34         super(b);
     35         resolver = new MemberResolver(cp);
     36         thisClass = cc;
     37         thisMethod = null;
     38     }
     39 
     40     /**
     41      * Returns the major version of the class file
     42      * targeted by this compilation.
     43      */
     44     public int getMajorVersion() {
     45         ClassFile cf = thisClass.getClassFile2();
     46         if (cf == null)
     47             return ClassFile.MAJOR_VERSION;     // JDK 1.3
     48         else
     49             return cf.getMajorVersion();
     50     }
     51 
     52     /**
     53      * Records the currently compiled method.
     54      */
     55     public void setThisMethod(CtMethod m) {
     56         thisMethod = m.getMethodInfo2();
     57         if (typeChecker != null)
     58             typeChecker.setThisMethod(thisMethod);
     59     }
     60 
     61     public CtClass getThisClass() { return thisClass; }
     62 
     63     /**
     64      * Returns the JVM-internal representation of this class name.
     65      */
     66     protected String getThisName() {
     67         return MemberResolver.javaToJvmName(thisClass.getName());
     68     }
     69 
     70     /**
     71      * Returns the JVM-internal representation of this super class name.
     72      */
     73     protected String getSuperName() throws CompileError {
     74         return MemberResolver.javaToJvmName(
     75                         MemberResolver.getSuperclass(thisClass).getName());
     76     }
     77 
     78     protected void insertDefaultSuperCall() throws CompileError {
     79         bytecode.addAload(0);
     80         bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass),
     81                                   "<init>", "()V");
     82     }
     83 
     84     static class JsrHook extends ReturnHook {
     85         ArrayList jsrList;
     86         CodeGen cgen;
     87         int var;
     88 
     89         JsrHook(CodeGen gen) {
     90             super(gen);
     91             jsrList = new ArrayList();
     92             cgen = gen;
     93             var = -1;
     94         }
     95 
     96         private int getVar(int size) {
     97             if (var < 0) {
     98                 var = cgen.getMaxLocals();
     99                 cgen.incMaxLocals(size);
    100             }
    101 
    102             return var;
    103         }
    104 
    105         private void jsrJmp(Bytecode b) {
    106             b.addOpcode(Opcode.GOTO);
    107             jsrList.add(new int[] {b.currentPc(), var});
    108             b.addIndex(0);
    109         }
    110 
    111         protected boolean doit(Bytecode b, int opcode) {
    112             switch (opcode) {
    113             case Opcode.RETURN :
    114                 jsrJmp(b);
    115                 break;
    116             case ARETURN :
    117                 b.addAstore(getVar(1));
    118                 jsrJmp(b);
    119                 b.addAload(var);
    120                 break;
    121             case IRETURN :
    122                 b.addIstore(getVar(1));
    123                 jsrJmp(b);
    124                 b.addIload(var);
    125                 break;
    126             case LRETURN :
    127                 b.addLstore(getVar(2));
    128                 jsrJmp(b);
    129                 b.addLload(var);
    130                 break;
    131             case DRETURN :
    132                 b.addDstore(getVar(2));
    133                 jsrJmp(b);
    134                 b.addDload(var);
    135                 break;
    136             case FRETURN :
    137                 b.addFstore(getVar(1));
    138                 jsrJmp(b);
    139                 b.addFload(var);
    140                 break;
    141             default :
    142                 throw new RuntimeException("fatal");
    143             }
    144 
    145             return false;
    146         }
    147     }
    148 
    149     static class JsrHook2 extends ReturnHook {
    150         int var;
    151         int target;
    152 
    153         JsrHook2(CodeGen gen, int[] retTarget) {
    154             super(gen);
    155             target = retTarget[0];
    156             var = retTarget[1];
    157         }
    158 
    159         protected boolean doit(Bytecode b, int opcode) {
    160             switch (opcode) {
    161             case Opcode.RETURN :
    162                 break;
    163             case ARETURN :
    164                 b.addAstore(var);
    165                 break;
    166             case IRETURN :
    167                 b.addIstore(var);
    168                 break;
    169             case LRETURN :
    170                 b.addLstore(var);
    171                 break;
    172             case DRETURN :
    173                 b.addDstore(var);
    174                 break;
    175             case FRETURN :
    176                 b.addFstore(var);
    177                 break;
    178             default :
    179                 throw new RuntimeException("fatal");
    180             }
    181 
    182             b.addOpcode(Opcode.GOTO);
    183             b.addIndex(target - b.currentPc() + 3);
    184             return true;
    185         }
    186     }
    187 
    188     protected void atTryStmnt(Stmnt st) throws CompileError {
    189         Bytecode bc = bytecode;
    190         Stmnt body = (Stmnt)st.getLeft();
    191         if (body == null)
    192             return;
    193 
    194         ASTList catchList = (ASTList)st.getRight().getLeft();
    195         Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft();
    196         ArrayList gotoList = new ArrayList();
    197 
    198         JsrHook jsrHook = null;
    199         if (finallyBlock != null)
    200             jsrHook = new JsrHook(this);
    201 
    202         int start = bc.currentPc();
    203         body.accept(this);
    204         int end = bc.currentPc();
    205         if (start == end)
    206             throw new CompileError("empty try block");
    207 
    208         boolean tryNotReturn = !hasReturned;
    209         if (tryNotReturn) {
    210             bc.addOpcode(Opcode.GOTO);
    211             gotoList.add(new Integer(bc.currentPc()));
    212             bc.addIndex(0);   // correct later
    213         }
    214 
    215         int var = getMaxLocals();
    216         incMaxLocals(1);
    217         while (catchList != null) {
    218             // catch clause
    219             Pair p = (Pair)catchList.head();
    220             catchList = catchList.tail();
    221             Declarator decl = (Declarator)p.getLeft();
    222             Stmnt block = (Stmnt)p.getRight();
    223 
    224             decl.setLocalVar(var);
    225 
    226             CtClass type = resolver.lookupClassByJvmName(decl.getClassName());
    227             decl.setClassName(MemberResolver.javaToJvmName(type.getName()));
    228             bc.addExceptionHandler(start, end, bc.currentPc(), type);
    229             bc.growStack(1);
    230             bc.addAstore(var);
    231             hasReturned = false;
    232             if (block != null)
    233                 block.accept(this);
    234 
    235             if (!hasReturned) {
    236                 bc.addOpcode(Opcode.GOTO);
    237                 gotoList.add(new Integer(bc.currentPc()));
    238                 bc.addIndex(0);   // correct later
    239                 tryNotReturn = true;
    240             }
    241         }
    242 
    243         if (finallyBlock != null) {
    244             jsrHook.remove(this);
    245             // catch (any) clause
    246             int pcAnyCatch = bc.currentPc();
    247             bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
    248             bc.growStack(1);
    249             bc.addAstore(var);
    250             hasReturned = false;
    251             finallyBlock.accept(this);
    252             if (!hasReturned) {
    253                 bc.addAload(var);
    254                 bc.addOpcode(ATHROW);
    255             }
    256 
    257             addFinally(jsrHook.jsrList, finallyBlock);
    258         }
    259 
    260         int pcEnd = bc.currentPc();
    261         patchGoto(gotoList, pcEnd);
    262         hasReturned = !tryNotReturn;
    263         if (finallyBlock != null) {
    264             if (tryNotReturn)
    265                 finallyBlock.accept(this);
    266         }
    267     }
    268 
    269     /**
    270      * Adds a finally clause for earch return statement.
    271      */
    272     private void addFinally(ArrayList returnList, Stmnt finallyBlock)
    273         throws CompileError
    274     {
    275         Bytecode bc = bytecode;
    276         int n = returnList.size();
    277         for (int i = 0; i < n; ++i) {
    278             final int[] ret = (int[])returnList.get(i);
    279             int pc = ret[0];
    280             bc.write16bit(pc, bc.currentPc() - pc + 1);
    281             ReturnHook hook = new JsrHook2(this, ret);
    282             finallyBlock.accept(this);
    283             hook.remove(this);
    284             if (!hasReturned) {
    285                 bc.addOpcode(Opcode.GOTO);
    286                 bc.addIndex(pc + 3 - bc.currentPc());
    287             }
    288         }
    289     }
    290 
    291     public void atNewExpr(NewExpr expr) throws CompileError {
    292         if (expr.isArray())
    293             atNewArrayExpr(expr);
    294         else {
    295             CtClass clazz = resolver.lookupClassByName(expr.getClassName());
    296             String cname = clazz.getName();
    297             ASTList args = expr.getArguments();
    298             bytecode.addNew(cname);
    299             bytecode.addOpcode(DUP);
    300 
    301             atMethodCallCore(clazz, MethodInfo.nameInit, args,
    302                              false, true, -1, null);
    303 
    304             exprType = CLASS;
    305             arrayDim = 0;
    306             className = MemberResolver.javaToJvmName(cname);
    307         }
    308     }
    309 
    310     public void atNewArrayExpr(NewExpr expr) throws CompileError {
    311         int type = expr.getArrayType();
    312         ASTList size = expr.getArraySize();
    313         ASTList classname = expr.getClassName();
    314         ArrayInit init = expr.getInitializer();
    315         if (size.length() > 1) {
    316             if (init != null)
    317                 throw new CompileError(
    318                         "sorry, multi-dimensional array initializer " +
    319                         "for new is not supported");
    320 
    321             atMultiNewArray(type, classname, size);
    322             return;
    323         }
    324 
    325         ASTree sizeExpr = size.head();
    326         atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init);
    327     }
    328 
    329     private void atNewArrayExpr2(int type, ASTree sizeExpr,
    330                         String jvmClassname, ArrayInit init) throws CompileError {
    331         if (init == null)
    332             if (sizeExpr == null)
    333                 throw new CompileError("no array size");
    334             else
    335                 sizeExpr.accept(this);
    336         else
    337             if (sizeExpr == null) {
    338                 int s = init.length();
    339                 bytecode.addIconst(s);
    340             }
    341             else
    342                 throw new CompileError("unnecessary array size specified for new");
    343 
    344         String elementClass;
    345         if (type == CLASS) {
    346             elementClass = resolveClassName(jvmClassname);
    347             bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass));
    348         }
    349         else {
    350             elementClass = null;
    351             int atype = 0;
    352             switch (type) {
    353             case BOOLEAN :
    354                 atype = T_BOOLEAN;
    355                 break;
    356             case CHAR :
    357                 atype = T_CHAR;
    358                 break;
    359             case FLOAT :
    360                 atype = T_FLOAT;
    361                 break;
    362             case DOUBLE :
    363                 atype = T_DOUBLE;
    364                 break;
    365             case BYTE :
    366                 atype = T_BYTE;
    367                 break;
    368             case SHORT :
    369                 atype = T_SHORT;
    370                 break;
    371             case INT :
    372                 atype = T_INT;
    373                 break;
    374             case LONG :
    375                 atype = T_LONG;
    376                 break;
    377             default :
    378                 badNewExpr();
    379                 break;
    380             }
    381 
    382             bytecode.addOpcode(NEWARRAY);
    383             bytecode.add(atype);
    384         }
    385 
    386         if (init != null) {
    387             int s = init.length();
    388             ASTList list = init;
    389             for (int i = 0; i < s; i++) {
    390                 bytecode.addOpcode(DUP);
    391                 bytecode.addIconst(i);
    392                 list.head().accept(this);
    393                 if (!isRefType(type))
    394                     atNumCastExpr(exprType, type);
    395 
    396                 bytecode.addOpcode(getArrayWriteOp(type, 0));
    397                 list = list.tail();
    398             }
    399         }
    400 
    401         exprType = type;
    402         arrayDim = 1;
    403         className = elementClass;
    404     }
    405 
    406     private static void badNewExpr() throws CompileError {
    407         throw new CompileError("bad new expression");
    408     }
    409 
    410     protected void atArrayVariableAssign(ArrayInit init, int varType,
    411                                          int varArray, String varClass) throws CompileError {
    412         atNewArrayExpr2(varType, null, varClass, init);
    413     }
    414 
    415     public void atArrayInit(ArrayInit init) throws CompileError {
    416         throw new CompileError("array initializer is not supported");
    417     }
    418 
    419     protected void atMultiNewArray(int type, ASTList classname, ASTList size)
    420         throws CompileError
    421     {
    422         int count, dim;
    423         dim = size.length();
    424         for (count = 0; size != null; size = size.tail()) {
    425             ASTree s = size.head();
    426             if (s == null)
    427                 break;          // int[][][] a = new int[3][4][];
    428 
    429             ++count;
    430             s.accept(this);
    431             if (exprType != INT)
    432                 throw new CompileError("bad type for array size");
    433         }
    434 
    435         String desc;
    436         exprType = type;
    437         arrayDim = dim;
    438         if (type == CLASS) {
    439             className = resolveClassName(classname);
    440             desc = toJvmArrayName(className, dim);
    441         }
    442         else
    443             desc = toJvmTypeName(type, dim);
    444 
    445         bytecode.addMultiNewarray(desc, count);
    446     }
    447 
    448     public void atCallExpr(CallExpr expr) throws CompileError {
    449         String mname = null;
    450         CtClass targetClass = null;
    451         ASTree method = expr.oprand1();
    452         ASTList args = (ASTList)expr.oprand2();
    453         boolean isStatic = false;
    454         boolean isSpecial = false;
    455         int aload0pos = -1;
    456 
    457         MemberResolver.Method cached = expr.getMethod();
    458         if (method instanceof Member) {
    459             mname = ((Member)method).get();
    460             targetClass = thisClass;
    461             if (inStaticMethod || (cached != null && cached.isStatic()))
    462                 isStatic = true;            // should be static
    463             else {
    464                 aload0pos = bytecode.currentPc();
    465                 bytecode.addAload(0);       // this
    466             }
    467         }
    468         else if (method instanceof Keyword) {   // constructor
    469             isSpecial = true;
    470             mname = MethodInfo.nameInit;        // <init>
    471             targetClass = thisClass;
    472             if (inStaticMethod)
    473                 throw new CompileError("a constructor cannot be static");
    474             else
    475                 bytecode.addAload(0);   // this
    476 
    477             if (((Keyword)method).get() == SUPER)
    478                 targetClass = MemberResolver.getSuperclass(targetClass);
    479         }
    480         else if (method instanceof Expr) {
    481             Expr e = (Expr)method;
    482             mname = ((Symbol)e.oprand2()).get();
    483             int op = e.getOperator();
    484             if (op == MEMBER) {                 // static method
    485                 targetClass
    486                     = resolver.lookupClass(((Symbol)e.oprand1()).get(), false);
    487                 isStatic = true;
    488             }
    489             else if (op == '.') {
    490                 ASTree target = e.oprand1();
    491                 if (target instanceof Keyword)
    492                     if (((Keyword)target).get() == SUPER)
    493                         isSpecial = true;
    494 
    495                 try {
    496                     target.accept(this);
    497                 }
    498                 catch (NoFieldException nfe) {
    499                     if (nfe.getExpr() != target)
    500                         throw nfe;
    501 
    502                     // it should be a static method.
    503                     exprType = CLASS;
    504                     arrayDim = 0;
    505                     className = nfe.getField(); // JVM-internal
    506                     resolver.recordPackage(className);
    507                     isStatic = true;
    508                 }
    509 
    510                 if (arrayDim > 0)
    511                     targetClass = resolver.lookupClass(javaLangObject, true);
    512                 else if (exprType == CLASS /* && arrayDim == 0 */)
    513                     targetClass = resolver.lookupClassByJvmName(className);
    514                 else
    515                     badMethod();
    516             }
    517             else
    518                 badMethod();
    519         }
    520         else
    521             fatal();
    522 
    523         atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
    524                          aload0pos, cached);
    525     }
    526 
    527     private static void badMethod() throws CompileError {
    528         throw new CompileError("bad method");
    529     }
    530 
    531     /*
    532      * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
    533      *
    534      * @param targetClass       the class at which method lookup starts.
    535      * @param found         not null if the method look has been already done.
    536      */
    537     public void atMethodCallCore(CtClass targetClass, String mname,
    538                         ASTList args, boolean isStatic, boolean isSpecial,
    539                         int aload0pos, MemberResolver.Method found)
    540         throws CompileError
    541     {
    542         int nargs = getMethodArgsLength(args);
    543         int[] types = new int[nargs];
    544         int[] dims = new int[nargs];
    545         String[] cnames = new String[nargs];
    546 
    547         if (!isStatic && found != null && found.isStatic()) {
    548             bytecode.addOpcode(POP);
    549             isStatic = true;
    550         }
    551 
    552         int stack = bytecode.getStackDepth();
    553 
    554         // generate code for evaluating arguments.
    555         atMethodArgs(args, types, dims, cnames);
    556 
    557         // used by invokeinterface
    558         int count = bytecode.getStackDepth() - stack + 1;
    559 
    560         if (found == null)
    561             found = resolver.lookupMethod(targetClass, thisClass, thisMethod,
    562                                           mname, types, dims, cnames);
    563 
    564         if (found == null) {
    565             String msg;
    566             if (mname.equals(MethodInfo.nameInit))
    567                 msg = "constructor not found";
    568             else
    569                 msg = "Method " + mname + " not found in "
    570                     + targetClass.getName();
    571 
    572             throw new CompileError(msg);
    573         }
    574 
    575         atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
    576                           aload0pos, count, found);
    577     }
    578 
    579     private void atMethodCallCore2(CtClass targetClass, String mname,
    580                                    boolean isStatic, boolean isSpecial,
    581                                    int aload0pos, int count,
    582                                    MemberResolver.Method found)
    583         throws CompileError
    584     {
    585         CtClass declClass = found.declaring;
    586         MethodInfo minfo = found.info;
    587         String desc = minfo.getDescriptor();
    588         int acc = minfo.getAccessFlags();
    589 
    590         if (mname.equals(MethodInfo.nameInit)) {
    591             isSpecial = true;
    592             if (declClass != targetClass)
    593                 throw new CompileError("no such constructor");
    594 
    595             if (declClass != thisClass && AccessFlag.isPrivate(acc)) {
    596                 desc = getAccessibleConstructor(desc, declClass, minfo);
    597                 bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
    598             }
    599         }
    600         else if (AccessFlag.isPrivate(acc))
    601             if (declClass == thisClass)
    602                 isSpecial = true;
    603             else {
    604                 isSpecial = false;
    605                 isStatic = true;
    606                 String origDesc = desc;
    607                 if ((acc & AccessFlag.STATIC) == 0)
    608                     desc = Descriptor.insertParameter(declClass.getName(),
    609                                                       origDesc);
    610 
    611                 acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
    612                 mname = getAccessiblePrivate(mname, origDesc, desc,
    613                                              minfo, declClass);
    614             }
    615 
    616         boolean popTarget = false;
    617         if ((acc & AccessFlag.STATIC) != 0) {
    618             if (!isStatic) {
    619                 /* this method is static but the target object is
    620                    on stack.  It must be popped out.  If aload0pos >= 0,
    621                    then the target object was pushed by aload_0.  It is
    622                    overwritten by NOP.
    623                 */
    624                 isStatic = true;
    625                 if (aload0pos >= 0)
    626                     bytecode.write(aload0pos, NOP);
    627                 else
    628                     popTarget = true;
    629             }
    630 
    631             bytecode.addInvokestatic(declClass, mname, desc);
    632         }
    633         else if (isSpecial)    // if (isSpecial && notStatic(acc))
    634             bytecode.addInvokespecial(declClass, mname, desc);
    635         else {
    636             if (!Modifier.isPublic(declClass.getModifiers())
    637                 || declClass.isInterface() != targetClass.isInterface())
    638                 declClass = targetClass;
    639 
    640             if (declClass.isInterface())
    641                 bytecode.addInvokeinterface(declClass, mname, desc, count);
    642             else
    643                 if (isStatic)
    644                     throw new CompileError(mname + " is not static");
    645                 else
    646                     bytecode.addInvokevirtual(declClass, mname, desc);
    647         }
    648 
    649         setReturnType(desc, isStatic, popTarget);
    650     }
    651 
    652     /*
    653      * Finds (or adds if necessary) a hidden accessor if the method
    654      * is in an enclosing class.
    655      *
    656      * @param desc          the descriptor of the method.
    657      * @param declClass     the class declaring the method.
    658      */
    659     protected String getAccessiblePrivate(String methodName, String desc,
    660                                           String newDesc, MethodInfo minfo,
    661                                           CtClass declClass)
    662         throws CompileError
    663     {
    664         if (isEnclosing(declClass, thisClass)) {
    665             AccessorMaker maker = declClass.getAccessorMaker();
    666             if (maker != null)
    667                 return maker.getMethodAccessor(methodName, desc, newDesc,
    668                                                minfo);
    669         }
    670 
    671         throw new CompileError("Method " + methodName
    672                                + " is private");
    673     }
    674 
    675     /*
    676      * Finds (or adds if necessary) a hidden constructor if the given
    677      * constructor is in an enclosing class.
    678      *
    679      * @param desc          the descriptor of the constructor.
    680      * @param declClass     the class declaring the constructor.
    681      * @param minfo         the method info of the constructor.
    682      * @return the descriptor of the hidden constructor.
    683      */
    684     protected String getAccessibleConstructor(String desc, CtClass declClass,
    685                                               MethodInfo minfo)
    686         throws CompileError
    687     {
    688         if (isEnclosing(declClass, thisClass)) {
    689             AccessorMaker maker = declClass.getAccessorMaker();
    690             if (maker != null)
    691                 return maker.getConstructor(declClass, desc, minfo);
    692         }
    693 
    694         throw new CompileError("the called constructor is private in "
    695                                + declClass.getName());
    696     }
    697 
    698     private boolean isEnclosing(CtClass outer, CtClass inner) {
    699         try {
    700             while (inner != null) {
    701                 inner = inner.getDeclaringClass();
    702                 if (inner == outer)
    703                     return true;
    704             }
    705         }
    706         catch (NotFoundException e) {}
    707         return false;
    708     }
    709 
    710     public int getMethodArgsLength(ASTList args) {
    711         return ASTList.length(args);
    712     }
    713 
    714     public void atMethodArgs(ASTList args, int[] types, int[] dims,
    715                              String[] cnames) throws CompileError {
    716         int i = 0;
    717         while (args != null) {
    718             ASTree a = args.head();
    719             a.accept(this);
    720             types[i] = exprType;
    721             dims[i] = arrayDim;
    722             cnames[i] = className;
    723             ++i;
    724             args = args.tail();
    725         }
    726     }
    727 
    728     void setReturnType(String desc, boolean isStatic, boolean popTarget)
    729         throws CompileError
    730     {
    731         int i = desc.indexOf(')');
    732         if (i < 0)
    733             badMethod();
    734 
    735         char c = desc.charAt(++i);
    736         int dim = 0;
    737         while (c == '[') {
    738             ++dim;
    739             c = desc.charAt(++i);
    740         }
    741 
    742         arrayDim = dim;
    743         if (c == 'L') {
    744             int j = desc.indexOf(';', i + 1);
    745             if (j < 0)
    746                 badMethod();
    747 
    748             exprType = CLASS;
    749             className = desc.substring(i + 1, j);
    750         }
    751         else {
    752             exprType = MemberResolver.descToType(c);
    753             className = null;
    754         }
    755 
    756         int etype = exprType;
    757         if (isStatic) {
    758             if (popTarget) {
    759                 if (is2word(etype, dim)) {
    760                     bytecode.addOpcode(DUP2_X1);
    761                     bytecode.addOpcode(POP2);
    762                     bytecode.addOpcode(POP);
    763                 }
    764                 else if (etype == VOID)
    765                     bytecode.addOpcode(POP);
    766                 else {
    767                     bytecode.addOpcode(SWAP);
    768                     bytecode.addOpcode(POP);
    769                 }
    770             }
    771         }
    772     }
    773 
    774     protected void atFieldAssign(Expr expr, int op, ASTree left,
    775                         ASTree right, boolean doDup) throws CompileError
    776     {
    777         CtField f = fieldAccess(left, false);
    778         boolean is_static = resultStatic;
    779         if (op != '=' && !is_static)
    780             bytecode.addOpcode(DUP);
    781 
    782         int fi;
    783         if (op == '=') {
    784             FieldInfo finfo = f.getFieldInfo2();
    785             setFieldType(finfo);
    786             AccessorMaker maker = isAccessibleField(f, finfo);
    787             if (maker == null)
    788                 fi = addFieldrefInfo(f, finfo);
    789             else
    790                 fi = 0;
    791         }
    792         else
    793             fi = atFieldRead(f, is_static);
    794 
    795         int fType = exprType;
    796         int fDim = arrayDim;
    797         String cname = className;
    798 
    799         atAssignCore(expr, op, right, fType, fDim, cname);
    800 
    801         boolean is2w = is2word(fType, fDim);
    802         if (doDup) {
    803             int dup_code;
    804             if (is_static)
    805                 dup_code = (is2w ? DUP2 : DUP);
    806             else
    807                 dup_code = (is2w ? DUP2_X1 : DUP_X1);
    808 
    809             bytecode.addOpcode(dup_code);
    810         }
    811 
    812         atFieldAssignCore(f, is_static, fi, is2w);
    813 
    814         exprType = fType;
    815         arrayDim = fDim;
    816         className = cname;
    817     }
    818 
    819     /* If fi == 0, the field must be a private field in an enclosing class.
    820      */
    821     private void atFieldAssignCore(CtField f, boolean is_static, int fi,
    822                                    boolean is2byte) throws CompileError {
    823         if (fi != 0) {
    824             if (is_static) {
    825                bytecode.add(PUTSTATIC);
    826                bytecode.growStack(is2byte ? -2 : -1);
    827             }
    828             else {
    829                 bytecode.add(PUTFIELD);
    830                 bytecode.growStack(is2byte ? -3 : -2);
    831             }
    832 
    833             bytecode.addIndex(fi);
    834         }
    835         else {
    836             CtClass declClass = f.getDeclaringClass();
    837             AccessorMaker maker = declClass.getAccessorMaker();
    838             // make should be non null.
    839             FieldInfo finfo = f.getFieldInfo2();
    840             MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
    841             bytecode.addInvokestatic(declClass, minfo.getName(),
    842                                      minfo.getDescriptor());
    843         }
    844     }
    845 
    846     /* overwritten in JvstCodeGen.
    847      */
    848     public void atMember(Member mem) throws CompileError {
    849         atFieldRead(mem);
    850     }
    851 
    852     protected void atFieldRead(ASTree expr) throws CompileError
    853     {
    854         CtField f = fieldAccess(expr, true);
    855         if (f == null) {
    856             atArrayLength(expr);
    857             return;
    858         }
    859 
    860         boolean is_static = resultStatic;
    861         ASTree cexpr = TypeChecker.getConstantFieldValue(f);
    862         if (cexpr == null)
    863             atFieldRead(f, is_static);
    864         else {
    865             cexpr.accept(this);
    866             setFieldType(f.getFieldInfo2());
    867         }
    868     }
    869 
    870     private void atArrayLength(ASTree expr) throws CompileError {
    871         if (arrayDim == 0)
    872             throw new CompileError(".length applied to a non array");
    873 
    874         bytecode.addOpcode(ARRAYLENGTH);
    875         exprType = INT;
    876         arrayDim = 0;
    877     }
    878 
    879     /**
    880      * Generates bytecode for reading a field value.
    881      * It returns a fieldref_info index or zero if the field is a private
    882      * one declared in an enclosing class.
    883      */
    884     private int atFieldRead(CtField f, boolean isStatic) throws CompileError {
    885         FieldInfo finfo = f.getFieldInfo2();
    886         boolean is2byte = setFieldType(finfo);
    887         AccessorMaker maker = isAccessibleField(f, finfo);
    888         if (maker != null) {
    889             MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
    890             bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(),
    891                                      minfo.getDescriptor());
    892             return 0;
    893         }
    894         else {
    895             int fi = addFieldrefInfo(f, finfo);
    896             if (isStatic) {
    897                 bytecode.add(GETSTATIC);
    898                 bytecode.growStack(is2byte ? 2 : 1);
    899             }
    900             else {
    901                 bytecode.add(GETFIELD);
    902                 bytecode.growStack(is2byte ? 1 : 0);
    903             }
    904 
    905             bytecode.addIndex(fi);
    906             return fi;
    907         }
    908     }
    909 
    910     /**
    911      * Returns null if the field is accessible.  Otherwise, it throws
    912      * an exception or it returns AccessorMaker if the field is a private
    913      * one declared in an enclosing class.
    914      */
    915     private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
    916         throws CompileError
    917     {
    918         if (AccessFlag.isPrivate(finfo.getAccessFlags())
    919             && f.getDeclaringClass() != thisClass) {
    920             CtClass declClass = f.getDeclaringClass();
    921             if (isEnclosing(declClass, thisClass)) {
    922                 AccessorMaker maker = declClass.getAccessorMaker();
    923                 if (maker != null)
    924                     return maker;
    925                 else
    926                     throw new CompileError("fatal error.  bug?");
    927             }
    928             else
    929                 throw new CompileError("Field " + f.getName() + " in "
    930                                        + declClass.getName() + " is private.");
    931         }
    932 
    933         return null;    // accessible field
    934     }
    935 
    936     /**
    937      * Sets exprType, arrayDim, and className.
    938      *
    939      * @return true if the field type is long or double.
    940      */
    941     private boolean setFieldType(FieldInfo finfo) throws CompileError {
    942         String type = finfo.getDescriptor();
    943 
    944         int i = 0;
    945         int dim = 0;
    946         char c = type.charAt(i);
    947         while (c == '[') {
    948             ++dim;
    949             c = type.charAt(++i);
    950         }
    951 
    952         arrayDim = dim;
    953         exprType = MemberResolver.descToType(c);
    954 
    955         if (c == 'L')
    956             className = type.substring(i + 1, type.indexOf(';', i + 1));
    957         else
    958             className = null;
    959 
    960         boolean is2byte = (c == 'J' || c == 'D');
    961         return is2byte;
    962     }
    963 
    964     private int addFieldrefInfo(CtField f, FieldInfo finfo) {
    965         ConstPool cp = bytecode.getConstPool();
    966         String cname = f.getDeclaringClass().getName();
    967         int ci = cp.addClassInfo(cname);
    968         String name = finfo.getName();
    969         String type = finfo.getDescriptor();
    970         return cp.addFieldrefInfo(ci, name, type);
    971     }
    972 
    973     protected void atClassObject2(String cname) throws CompileError {
    974         if (getMajorVersion() < ClassFile.JAVA_5)
    975             super.atClassObject2(cname);
    976         else
    977             bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname));
    978     }
    979 
    980     protected void atFieldPlusPlus(int token, boolean isPost,
    981                                    ASTree oprand, Expr expr, boolean doDup)
    982         throws CompileError
    983     {
    984         CtField f = fieldAccess(oprand, false);
    985         boolean is_static = resultStatic;
    986         if (!is_static)
    987             bytecode.addOpcode(DUP);
    988 
    989         int fi = atFieldRead(f, is_static);
    990         int t = exprType;
    991         boolean is2w = is2word(t, arrayDim);
    992 
    993         int dup_code;
    994         if (is_static)
    995             dup_code = (is2w ? DUP2 : DUP);
    996         else
    997             dup_code = (is2w ? DUP2_X1 : DUP_X1);
    998 
    999         atPlusPlusCore(dup_code, doDup, token, isPost, expr);
   1000         atFieldAssignCore(f, is_static, fi, is2w);
   1001     }
   1002 
   1003     /* This method also returns a value in resultStatic.
   1004      *
   1005      * @param acceptLength      true if array length is acceptable
   1006      */
   1007     protected CtField fieldAccess(ASTree expr, boolean acceptLength)
   1008             throws CompileError
   1009     {
   1010         if (expr instanceof Member) {
   1011             String name = ((Member)expr).get();
   1012             CtField f = null;
   1013             try {
   1014                 f = thisClass.getField(name);
   1015             }
   1016             catch (NotFoundException e) {
   1017                 // EXPR might be part of a static member access?
   1018                 throw new NoFieldException(name, expr);
   1019             }
   1020 
   1021             boolean is_static = Modifier.isStatic(f.getModifiers());
   1022             if (!is_static)
   1023                 if (inStaticMethod)
   1024                     throw new CompileError(
   1025                                 "not available in a static method: " + name);
   1026                 else
   1027                     bytecode.addAload(0);       // this
   1028 
   1029             resultStatic = is_static;
   1030             return f;
   1031         }
   1032         else if (expr instanceof Expr) {
   1033             Expr e = (Expr)expr;
   1034             int op = e.getOperator();
   1035             if (op == MEMBER) {
   1036                 /* static member by # (extension by Javassist)
   1037                  * For example, if int.class is parsed, the resulting tree
   1038                  * is (# "java.lang.Integer" "TYPE").
   1039                  */
   1040                 CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(),
   1041                                          (Symbol)e.oprand2());
   1042                 resultStatic = true;
   1043                 return f;
   1044             }
   1045             else if (op == '.') {
   1046                 CtField f = null;
   1047                 try {
   1048                     e.oprand1().accept(this);
   1049                     /* Don't call lookupFieldByJvmName2().
   1050                      * The left operand of . is not a class name but
   1051                      * a normal expression.
   1052                      */
   1053                     if (exprType == CLASS && arrayDim == 0)
   1054                         f = resolver.lookupFieldByJvmName(className,
   1055                                                     (Symbol)e.oprand2());
   1056                     else if (acceptLength && arrayDim > 0
   1057                              && ((Symbol)e.oprand2()).get().equals("length"))
   1058                         return null;    // expr is an array length.
   1059                     else
   1060                         badLvalue();
   1061 
   1062                     boolean is_static = Modifier.isStatic(f.getModifiers());
   1063                     if (is_static)
   1064                         bytecode.addOpcode(POP);
   1065 
   1066                     resultStatic = is_static;
   1067                     return f;
   1068                 }
   1069                 catch (NoFieldException nfe) {
   1070                     if (nfe.getExpr() != e.oprand1())
   1071                         throw nfe;
   1072 
   1073                     /* EXPR should be a static field.
   1074                      * If EXPR might be part of a qualified class name,
   1075                      * lookupFieldByJvmName2() throws NoFieldException.
   1076                      */
   1077                     Symbol fname = (Symbol)e.oprand2();
   1078                     String cname = nfe.getField();
   1079                     f = resolver.lookupFieldByJvmName2(cname, fname, expr);
   1080                     resolver.recordPackage(cname);
   1081                     resultStatic = true;
   1082                     return f;
   1083                 }
   1084             }
   1085             else
   1086                 badLvalue();
   1087         }
   1088         else
   1089             badLvalue();
   1090 
   1091         resultStatic = false;
   1092         return null;    // never reach
   1093     }
   1094 
   1095     private static void badLvalue() throws CompileError {
   1096         throw new CompileError("bad l-value");
   1097     }
   1098 
   1099     public CtClass[] makeParamList(MethodDecl md) throws CompileError {
   1100         CtClass[] params;
   1101         ASTList plist = md.getParams();
   1102         if (plist == null)
   1103             params = new CtClass[0];
   1104         else {
   1105             int i = 0;
   1106             params = new CtClass[plist.length()];
   1107             while (plist != null) {
   1108                 params[i++] = resolver.lookupClass((Declarator)plist.head());
   1109                 plist = plist.tail();
   1110             }
   1111         }
   1112 
   1113         return params;
   1114     }
   1115 
   1116     public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
   1117         CtClass[] clist;
   1118         ASTList list = md.getThrows();
   1119         if (list == null)
   1120             return null;
   1121         else {
   1122             int i = 0;
   1123             clist = new CtClass[list.length()];
   1124             while (list != null) {
   1125                 clist[i++] = resolver.lookupClassByName((ASTList)list.head());
   1126                 list = list.tail();
   1127             }
   1128 
   1129             return clist;
   1130         }
   1131     }
   1132 
   1133     /* Converts a class name into a JVM-internal representation.
   1134      *
   1135      * It may also expand a simple class name to java.lang.*.
   1136      * For example, this converts Object into java/lang/Object.
   1137      */
   1138     protected String resolveClassName(ASTList name) throws CompileError {
   1139         return resolver.resolveClassName(name);
   1140     }
   1141 
   1142     /* Expands a simple class name to java.lang.*.
   1143      * For example, this converts Object into java/lang/Object.
   1144      */
   1145     protected String resolveClassName(String jvmName) throws CompileError {
   1146         return resolver.resolveJvmClassName(jvmName);
   1147     }
   1148 }
   1149