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 java.util.ArrayList;
     19 import java.util.Arrays;
     20 import javassist.compiler.ast.*;
     21 import javassist.bytecode.*;
     22 
     23 /* The code generator is implemeted by three files:
     24  * CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
     25  * I just wanted to split a big file into three smaller ones.
     26  */
     27 
     28 public abstract class CodeGen extends Visitor implements Opcode, TokenId {
     29     static final String javaLangObject = "java.lang.Object";
     30     static final String jvmJavaLangObject = "java/lang/Object";
     31 
     32     static final String javaLangString = "java.lang.String";
     33     static final String jvmJavaLangString = "java/lang/String";
     34 
     35     protected Bytecode bytecode;
     36     private int tempVar;
     37     TypeChecker typeChecker;
     38 
     39     /**
     40      * true if the last visited node is a return statement.
     41      */
     42     protected boolean hasReturned;
     43 
     44     /**
     45      * Must be true if compilation is for a static method.
     46      */
     47     public boolean inStaticMethod;
     48 
     49     protected ArrayList breakList, continueList;
     50 
     51     /**
     52      * doit() in ReturnHook is called from atReturn().
     53      */
     54     protected static abstract class ReturnHook {
     55         ReturnHook next;
     56 
     57         /**
     58          * Returns true if the generated code ends with return,
     59          * throw, or goto.
     60          */
     61         protected abstract boolean doit(Bytecode b, int opcode);
     62 
     63         protected ReturnHook(CodeGen gen) {
     64             next = gen.returnHooks;
     65             gen.returnHooks = this;
     66         }
     67 
     68         protected void remove(CodeGen gen) {
     69             gen.returnHooks = next;
     70         }
     71     }
     72 
     73     protected ReturnHook returnHooks;
     74 
     75     /* The following fields are used by atXXX() methods
     76      * for returning the type of the compiled expression.
     77      */
     78     protected int exprType;     // VOID, NULL, CLASS, BOOLEAN, INT, ...
     79     protected int arrayDim;
     80     protected String className; // JVM-internal representation
     81 
     82     public CodeGen(Bytecode b) {
     83         bytecode = b;
     84         tempVar = -1;
     85         typeChecker = null;
     86         hasReturned = false;
     87         inStaticMethod = false;
     88         breakList = null;
     89         continueList = null;
     90         returnHooks = null;
     91     }
     92 
     93     public void setTypeChecker(TypeChecker checker) {
     94         typeChecker = checker;
     95     }
     96 
     97     protected static void fatal() throws CompileError {
     98         throw new CompileError("fatal");
     99     }
    100 
    101     public static boolean is2word(int type, int dim) {
    102         return dim == 0 && (type == DOUBLE || type == LONG);
    103     }
    104 
    105     public int getMaxLocals() { return bytecode.getMaxLocals(); }
    106 
    107     public void setMaxLocals(int n) {
    108         bytecode.setMaxLocals(n);
    109     }
    110 
    111     protected void incMaxLocals(int size) {
    112         bytecode.incMaxLocals(size);
    113     }
    114 
    115     /**
    116      * Returns a local variable that single or double words can be
    117      * stored in.
    118      */
    119     protected int getTempVar() {
    120         if (tempVar < 0) {
    121             tempVar = getMaxLocals();
    122             incMaxLocals(2);
    123         }
    124 
    125         return tempVar;
    126     }
    127 
    128     protected int getLocalVar(Declarator d) {
    129         int v = d.getLocalVar();
    130         if (v < 0) {
    131             v = getMaxLocals(); // delayed variable allocation.
    132             d.setLocalVar(v);
    133             incMaxLocals(1);
    134         }
    135 
    136         return v;
    137     }
    138 
    139     /**
    140      * Returns the JVM-internal representation of this class name.
    141      */
    142     protected abstract String getThisName();
    143 
    144     /**
    145      * Returns the JVM-internal representation of this super class name.
    146      */
    147     protected abstract String getSuperName() throws CompileError;
    148 
    149     /* Converts a class name into a JVM-internal representation.
    150      *
    151      * It may also expand a simple class name to java.lang.*.
    152      * For example, this converts Object into java/lang/Object.
    153      */
    154     protected abstract String resolveClassName(ASTList name)
    155         throws CompileError;
    156 
    157     /* Expands a simple class name to java.lang.*.
    158      * For example, this converts Object into java/lang/Object.
    159      */
    160     protected abstract String resolveClassName(String jvmClassName)
    161         throws CompileError;
    162 
    163     /**
    164      * @param name      the JVM-internal representation.
    165      *                  name is not exapnded to java.lang.*.
    166      */
    167     protected static String toJvmArrayName(String name, int dim) {
    168         if (name == null)
    169             return null;
    170 
    171         if (dim == 0)
    172             return name;
    173         else {
    174             StringBuffer sbuf = new StringBuffer();
    175             int d = dim;
    176             while (d-- > 0)
    177                 sbuf.append('[');
    178 
    179             sbuf.append('L');
    180             sbuf.append(name);
    181             sbuf.append(';');
    182 
    183             return sbuf.toString();
    184         }
    185     }
    186 
    187     protected static String toJvmTypeName(int type, int dim) {
    188         char c = 'I';
    189         switch(type) {
    190         case BOOLEAN :
    191             c = 'Z';
    192             break;
    193         case BYTE :
    194             c = 'B';
    195             break;
    196         case CHAR :
    197             c = 'C';
    198             break;
    199         case SHORT :
    200             c = 'S';
    201             break;
    202         case INT :
    203             c = 'I';
    204             break;
    205         case LONG :
    206             c = 'J';
    207             break;
    208         case FLOAT :
    209             c = 'F';
    210             break;
    211         case DOUBLE :
    212             c = 'D';
    213             break;
    214         case VOID :
    215             c = 'V';
    216             break;
    217         }
    218 
    219         StringBuffer sbuf = new StringBuffer();
    220         while (dim-- > 0)
    221                 sbuf.append('[');
    222 
    223         sbuf.append(c);
    224         return sbuf.toString();
    225     }
    226 
    227     public void compileExpr(ASTree expr) throws CompileError {
    228         doTypeCheck(expr);
    229         expr.accept(this);
    230     }
    231 
    232     public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
    233         throws CompileError
    234     {
    235         doTypeCheck(expr);
    236         return booleanExpr(branchIf, expr);
    237     }
    238 
    239     public void doTypeCheck(ASTree expr) throws CompileError {
    240         if (typeChecker != null)
    241             expr.accept(typeChecker);
    242     }
    243 
    244     public void atASTList(ASTList n) throws CompileError { fatal(); }
    245 
    246     public void atPair(Pair n) throws CompileError { fatal(); }
    247 
    248     public void atSymbol(Symbol n) throws CompileError { fatal(); }
    249 
    250     public void atFieldDecl(FieldDecl field) throws CompileError {
    251         field.getInit().accept(this);
    252     }
    253 
    254     public void atMethodDecl(MethodDecl method) throws CompileError {
    255         ASTList mods = method.getModifiers();
    256         setMaxLocals(1);
    257         while (mods != null) {
    258             Keyword k = (Keyword)mods.head();
    259             mods = mods.tail();
    260             if (k.get() == STATIC) {
    261                 setMaxLocals(0);
    262                 inStaticMethod = true;
    263             }
    264         }
    265 
    266         ASTList params = method.getParams();
    267         while (params != null) {
    268             atDeclarator((Declarator)params.head());
    269             params = params.tail();
    270         }
    271 
    272         Stmnt s = method.getBody();
    273         atMethodBody(s, method.isConstructor(),
    274                      method.getReturn().getType() == VOID);
    275     }
    276 
    277     /**
    278      * @param isCons	true if super() must be called.
    279      *			false if the method is a class initializer.
    280      */
    281     public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid)
    282         throws CompileError
    283     {
    284         if (s == null)
    285             return;
    286 
    287         if (isCons && needsSuperCall(s))
    288             insertDefaultSuperCall();
    289 
    290         hasReturned = false;
    291         s.accept(this);
    292         if (!hasReturned)
    293             if (isVoid) {
    294                 bytecode.addOpcode(Opcode.RETURN);
    295                 hasReturned = true;
    296             }
    297             else
    298                 throw new CompileError("no return statement");
    299     }
    300 
    301     private boolean needsSuperCall(Stmnt body) throws CompileError {
    302         if (body.getOperator() == BLOCK)
    303             body = (Stmnt)body.head();
    304 
    305         if (body != null && body.getOperator() == EXPR) {
    306             ASTree expr = body.head();
    307             if (expr != null && expr instanceof Expr
    308                 && ((Expr)expr).getOperator() == CALL) {
    309                 ASTree target = ((Expr)expr).head();
    310                 if (target instanceof Keyword) {
    311                     int token = ((Keyword)target).get();
    312                     return token != THIS && token != SUPER;
    313                 }
    314             }
    315         }
    316 
    317         return true;
    318     }
    319 
    320     protected abstract void insertDefaultSuperCall() throws CompileError;
    321 
    322     public void atStmnt(Stmnt st) throws CompileError {
    323         if (st == null)
    324             return;     // empty
    325 
    326         int op = st.getOperator();
    327         if (op == EXPR) {
    328             ASTree expr = st.getLeft();
    329             doTypeCheck(expr);
    330             if (expr instanceof AssignExpr)
    331                 atAssignExpr((AssignExpr)expr, false);
    332             else if (isPlusPlusExpr(expr)) {
    333                 Expr e = (Expr)expr;
    334                 atPlusPlus(e.getOperator(), e.oprand1(), e, false);
    335             }
    336             else {
    337                 expr.accept(this);
    338                 if (is2word(exprType, arrayDim))
    339                     bytecode.addOpcode(POP2);
    340                 else if (exprType != VOID)
    341                     bytecode.addOpcode(POP);
    342             }
    343         }
    344         else if (op == DECL || op == BLOCK) {
    345             ASTList list = st;
    346             while (list != null) {
    347                 ASTree h = list.head();
    348                 list = list.tail();
    349                 if (h != null)
    350                     h.accept(this);
    351             }
    352         }
    353         else if (op == IF)
    354             atIfStmnt(st);
    355         else if (op == WHILE || op == DO)
    356             atWhileStmnt(st, op == WHILE);
    357         else if (op == FOR)
    358             atForStmnt(st);
    359         else if (op == BREAK || op == CONTINUE)
    360             atBreakStmnt(st, op == BREAK);
    361         else if (op == TokenId.RETURN)
    362             atReturnStmnt(st);
    363         else if (op == THROW)
    364             atThrowStmnt(st);
    365         else if (op == TRY)
    366             atTryStmnt(st);
    367         else if (op == SWITCH)
    368             atSwitchStmnt(st);
    369         else if (op == SYNCHRONIZED)
    370             atSyncStmnt(st);
    371         else {
    372             // LABEL, SWITCH label stament might be null?.
    373             hasReturned = false;
    374             throw new CompileError(
    375                 "sorry, not supported statement: TokenId " + op);
    376         }
    377     }
    378 
    379     private void atIfStmnt(Stmnt st) throws CompileError {
    380         ASTree expr = st.head();
    381         Stmnt thenp = (Stmnt)st.tail().head();
    382         Stmnt elsep = (Stmnt)st.tail().tail().head();
    383         compileBooleanExpr(false, expr);
    384         int pc = bytecode.currentPc();
    385         int pc2 = 0;
    386         bytecode.addIndex(0);   // correct later
    387 
    388         hasReturned = false;
    389         if (thenp != null)
    390             thenp.accept(this);
    391 
    392         boolean thenHasReturned = hasReturned;
    393         hasReturned = false;
    394 
    395         if (elsep != null && !thenHasReturned) {
    396             bytecode.addOpcode(Opcode.GOTO);
    397             pc2 = bytecode.currentPc();
    398             bytecode.addIndex(0);
    399         }
    400 
    401         bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
    402 
    403         if (elsep != null) {
    404             elsep.accept(this);
    405             if (!thenHasReturned)
    406                 bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
    407 
    408             hasReturned = thenHasReturned && hasReturned;
    409         }
    410     }
    411 
    412     private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
    413         ArrayList prevBreakList = breakList;
    414         ArrayList prevContList = continueList;
    415         breakList = new ArrayList();
    416         continueList = new ArrayList();
    417 
    418         ASTree expr = st.head();
    419         Stmnt body = (Stmnt)st.tail();
    420 
    421         int pc = 0;
    422         if (notDo) {
    423             bytecode.addOpcode(Opcode.GOTO);
    424             pc = bytecode.currentPc();
    425             bytecode.addIndex(0);
    426         }
    427 
    428         int pc2 = bytecode.currentPc();
    429         if (body != null)
    430             body.accept(this);
    431 
    432         int pc3 = bytecode.currentPc();
    433         if (notDo)
    434             bytecode.write16bit(pc, pc3 - pc + 1);
    435 
    436         boolean alwaysBranch = compileBooleanExpr(true, expr);
    437         bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
    438 
    439         patchGoto(breakList, bytecode.currentPc());
    440         patchGoto(continueList, pc3);
    441         continueList = prevContList;
    442         breakList = prevBreakList;
    443         hasReturned = alwaysBranch;
    444     }
    445 
    446     protected void patchGoto(ArrayList list, int targetPc) {
    447         int n = list.size();
    448         for (int i = 0; i < n; ++i) {
    449             int pc = ((Integer)list.get(i)).intValue();
    450             bytecode.write16bit(pc, targetPc - pc + 1);
    451         }
    452     }
    453 
    454     private void atForStmnt(Stmnt st) throws CompileError {
    455         ArrayList prevBreakList = breakList;
    456         ArrayList prevContList = continueList;
    457         breakList = new ArrayList();
    458         continueList = new ArrayList();
    459 
    460         Stmnt init = (Stmnt)st.head();
    461         ASTList p = st.tail();
    462         ASTree expr = p.head();
    463         p = p.tail();
    464         Stmnt update = (Stmnt)p.head();
    465         Stmnt body = (Stmnt)p.tail();
    466 
    467         if (init != null)
    468             init.accept(this);
    469 
    470         int pc = bytecode.currentPc();
    471         int pc2 = 0;
    472         if (expr != null) {
    473             compileBooleanExpr(false, expr);
    474             pc2 = bytecode.currentPc();
    475             bytecode.addIndex(0);
    476         }
    477 
    478         if (body != null)
    479             body.accept(this);
    480 
    481         int pc3 = bytecode.currentPc();
    482         if (update != null)
    483             update.accept(this);
    484 
    485         bytecode.addOpcode(Opcode.GOTO);
    486         bytecode.addIndex(pc - bytecode.currentPc() + 1);
    487 
    488         int pc4 = bytecode.currentPc();
    489         if (expr != null)
    490             bytecode.write16bit(pc2, pc4 - pc2 + 1);
    491 
    492         patchGoto(breakList, pc4);
    493         patchGoto(continueList, pc3);
    494         continueList = prevContList;
    495         breakList = prevBreakList;
    496         hasReturned = false;
    497     }
    498 
    499     private void atSwitchStmnt(Stmnt st) throws CompileError {
    500         compileExpr(st.head());
    501 
    502         ArrayList prevBreakList = breakList;
    503         breakList = new ArrayList();
    504         int opcodePc = bytecode.currentPc();
    505         bytecode.addOpcode(LOOKUPSWITCH);
    506         int npads = 3 - (opcodePc & 3);
    507         while (npads-- > 0)
    508             bytecode.add(0);
    509 
    510         Stmnt body = (Stmnt)st.tail();
    511         int npairs = 0;
    512         for (ASTList list = body; list != null; list = list.tail())
    513             if (((Stmnt)list.head()).getOperator() == CASE)
    514                 ++npairs;
    515 
    516         // opcodePc2 is the position at which the default jump offset is.
    517         int opcodePc2 = bytecode.currentPc();
    518         bytecode.addGap(4);
    519         bytecode.add32bit(npairs);
    520         bytecode.addGap(npairs * 8);
    521 
    522         long[] pairs = new long[npairs];
    523         int ipairs = 0;
    524         int defaultPc = -1;
    525         for (ASTList list = body; list != null; list = list.tail()) {
    526             Stmnt label = (Stmnt)list.head();
    527             int op = label.getOperator();
    528             if (op == DEFAULT)
    529                 defaultPc = bytecode.currentPc();
    530             else if (op != CASE)
    531                 fatal();
    532             else {
    533                 pairs[ipairs++]
    534                     = ((long)computeLabel(label.head()) << 32) +
    535                       ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff);
    536             }
    537 
    538             hasReturned = false;
    539             ((Stmnt)label.tail()).accept(this);
    540         }
    541 
    542         Arrays.sort(pairs);
    543         int pc = opcodePc2 + 8;
    544         for (int i = 0; i < npairs; ++i) {
    545             bytecode.write32bit(pc, (int)(pairs[i] >>> 32));
    546             bytecode.write32bit(pc + 4, (int)pairs[i]);
    547             pc += 8;
    548         }
    549 
    550         if (defaultPc < 0 || breakList.size() > 0)
    551             hasReturned = false;
    552 
    553         int endPc = bytecode.currentPc();
    554         if (defaultPc < 0)
    555             defaultPc = endPc;
    556 
    557         bytecode.write32bit(opcodePc2, defaultPc - opcodePc);
    558 
    559         patchGoto(breakList, endPc);
    560         breakList = prevBreakList;
    561     }
    562 
    563     private int computeLabel(ASTree expr) throws CompileError {
    564         doTypeCheck(expr);
    565         expr = TypeChecker.stripPlusExpr(expr);
    566         if (expr instanceof IntConst)
    567             return (int)((IntConst)expr).get();
    568         else
    569             throw new CompileError("bad case label");
    570     }
    571 
    572     private void atBreakStmnt(Stmnt st, boolean notCont)
    573         throws CompileError
    574     {
    575         if (st.head() != null)
    576             throw new CompileError(
    577                         "sorry, not support labeled break or continue");
    578 
    579         bytecode.addOpcode(Opcode.GOTO);
    580         Integer pc = new Integer(bytecode.currentPc());
    581         bytecode.addIndex(0);
    582         if (notCont)
    583             breakList.add(pc);
    584         else
    585             continueList.add(pc);
    586     }
    587 
    588     protected void atReturnStmnt(Stmnt st) throws CompileError {
    589         atReturnStmnt2(st.getLeft());
    590     }
    591 
    592     protected final void atReturnStmnt2(ASTree result) throws CompileError {
    593         int op;
    594         if (result == null)
    595             op = Opcode.RETURN;
    596         else {
    597             compileExpr(result);
    598             if (arrayDim > 0)
    599                 op = ARETURN;
    600             else {
    601                 int type = exprType;
    602                 if (type == DOUBLE)
    603                     op = DRETURN;
    604                 else if (type == FLOAT)
    605                     op = FRETURN;
    606                 else if (type == LONG)
    607                     op = LRETURN;
    608                 else if (isRefType(type))
    609                     op = ARETURN;
    610                 else
    611                     op = IRETURN;
    612             }
    613         }
    614 
    615         for (ReturnHook har = returnHooks; har != null; har = har.next)
    616             if (har.doit(bytecode, op)) {
    617                 hasReturned = true;
    618                 return;
    619             }
    620 
    621         bytecode.addOpcode(op);
    622         hasReturned = true;
    623     }
    624 
    625     private void atThrowStmnt(Stmnt st) throws CompileError {
    626         ASTree e = st.getLeft();
    627         compileExpr(e);
    628         if (exprType != CLASS || arrayDim > 0)
    629             throw new CompileError("bad throw statement");
    630 
    631         bytecode.addOpcode(ATHROW);
    632         hasReturned = true;
    633     }
    634 
    635     /* overridden in MemberCodeGen
    636      */
    637     protected void atTryStmnt(Stmnt st) throws CompileError {
    638         hasReturned = false;
    639     }
    640 
    641     private void atSyncStmnt(Stmnt st) throws CompileError {
    642         int nbreaks = getListSize(breakList);
    643         int ncontinues = getListSize(continueList);
    644 
    645         compileExpr(st.head());
    646         if (exprType != CLASS && arrayDim == 0)
    647             throw new CompileError("bad type expr for synchronized block");
    648 
    649         Bytecode bc = bytecode;
    650         final int var = bc.getMaxLocals();
    651         bc.incMaxLocals(1);
    652         bc.addOpcode(DUP);
    653         bc.addAstore(var);
    654         bc.addOpcode(MONITORENTER);
    655 
    656         ReturnHook rh = new ReturnHook(this) {
    657             protected boolean doit(Bytecode b, int opcode) {
    658                 b.addAload(var);
    659                 b.addOpcode(MONITOREXIT);
    660                 return false;
    661             }
    662         };
    663 
    664         int pc = bc.currentPc();
    665         Stmnt body = (Stmnt)st.tail();
    666         if (body != null)
    667             body.accept(this);
    668 
    669         int pc2 = bc.currentPc();
    670         int pc3 = 0;
    671         if (!hasReturned) {
    672             rh.doit(bc, 0);     // the 2nd arg is ignored.
    673             bc.addOpcode(Opcode.GOTO);
    674             pc3 = bc.currentPc();
    675             bc.addIndex(0);
    676         }
    677 
    678         if (pc < pc2) {         // if the body is not empty
    679             int pc4 = bc.currentPc();
    680             rh.doit(bc, 0);         // the 2nd arg is ignored.
    681             bc.addOpcode(ATHROW);
    682             bc.addExceptionHandler(pc, pc2, pc4, 0);
    683         }
    684 
    685         if (!hasReturned)
    686             bc.write16bit(pc3, bc.currentPc() - pc3 + 1);
    687 
    688         rh.remove(this);
    689 
    690         if (getListSize(breakList) != nbreaks
    691             || getListSize(continueList) != ncontinues)
    692             throw new CompileError(
    693                 "sorry, cannot break/continue in synchronized block");
    694     }
    695 
    696     private static int getListSize(ArrayList list) {
    697         return list == null ? 0 : list.size();
    698     }
    699 
    700     private static boolean isPlusPlusExpr(ASTree expr) {
    701         if (expr instanceof Expr) {
    702             int op = ((Expr)expr).getOperator();
    703             return op == PLUSPLUS || op == MINUSMINUS;
    704         }
    705 
    706         return false;
    707     }
    708 
    709     public void atDeclarator(Declarator d) throws CompileError {
    710         d.setLocalVar(getMaxLocals());
    711         d.setClassName(resolveClassName(d.getClassName()));
    712 
    713         int size;
    714         if (is2word(d.getType(), d.getArrayDim()))
    715             size = 2;
    716         else
    717             size = 1;
    718 
    719         incMaxLocals(size);
    720 
    721         /*  NOTE: Array initializers has not been supported.
    722          */
    723         ASTree init = d.getInitializer();
    724         if (init != null) {
    725             doTypeCheck(init);
    726             atVariableAssign(null, '=', null, d, init, false);
    727         }
    728     }
    729 
    730     public abstract void atNewExpr(NewExpr n) throws CompileError;
    731 
    732     public abstract void atArrayInit(ArrayInit init) throws CompileError;
    733 
    734     public void atAssignExpr(AssignExpr expr) throws CompileError {
    735         atAssignExpr(expr, true);
    736     }
    737 
    738     protected void atAssignExpr(AssignExpr expr, boolean doDup)
    739         throws CompileError
    740     {
    741         // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
    742         int op = expr.getOperator();
    743         ASTree left = expr.oprand1();
    744         ASTree right = expr.oprand2();
    745         if (left instanceof Variable)
    746             atVariableAssign(expr, op, (Variable)left,
    747                              ((Variable)left).getDeclarator(),
    748                              right, doDup);
    749         else {
    750             if (left instanceof Expr) {
    751                 Expr e = (Expr)left;
    752                 if (e.getOperator() == ARRAY) {
    753                     atArrayAssign(expr, op, (Expr)left, right, doDup);
    754                     return;
    755                 }
    756             }
    757 
    758             atFieldAssign(expr, op, left, right, doDup);
    759         }
    760     }
    761 
    762     protected static void badAssign(Expr expr) throws CompileError {
    763         String msg;
    764         if (expr == null)
    765             msg = "incompatible type for assignment";
    766         else
    767             msg = "incompatible type for " + expr.getName();
    768 
    769         throw new CompileError(msg);
    770     }
    771 
    772     /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
    773      *
    774      * expr and var can be null.
    775      */
    776     private void atVariableAssign(Expr expr, int op, Variable var,
    777                                   Declarator d, ASTree right,
    778                                   boolean doDup) throws CompileError
    779     {
    780         int varType = d.getType();
    781         int varArray = d.getArrayDim();
    782         String varClass = d.getClassName();
    783         int varNo = getLocalVar(d);
    784 
    785         if (op != '=')
    786             atVariable(var);
    787 
    788         // expr is null if the caller is atDeclarator().
    789         if (expr == null && right instanceof ArrayInit)
    790             atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass);
    791         else
    792             atAssignCore(expr, op, right, varType, varArray, varClass);
    793 
    794         if (doDup)
    795             if (is2word(varType, varArray))
    796                 bytecode.addOpcode(DUP2);
    797             else
    798                 bytecode.addOpcode(DUP);
    799 
    800         if (varArray > 0)
    801             bytecode.addAstore(varNo);
    802         else if (varType == DOUBLE)
    803             bytecode.addDstore(varNo);
    804         else if (varType == FLOAT)
    805             bytecode.addFstore(varNo);
    806         else if (varType == LONG)
    807             bytecode.addLstore(varNo);
    808         else if (isRefType(varType))
    809             bytecode.addAstore(varNo);
    810         else
    811             bytecode.addIstore(varNo);
    812 
    813         exprType = varType;
    814         arrayDim = varArray;
    815         className = varClass;
    816     }
    817 
    818     protected abstract void atArrayVariableAssign(ArrayInit init,
    819             int varType, int varArray, String varClass) throws CompileError;
    820 
    821     private void atArrayAssign(Expr expr, int op, Expr array,
    822                         ASTree right, boolean doDup) throws CompileError
    823     {
    824         arrayAccess(array.oprand1(), array.oprand2());
    825 
    826         if (op != '=') {
    827             bytecode.addOpcode(DUP2);
    828             bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
    829         }
    830 
    831         int aType = exprType;
    832         int aDim = arrayDim;
    833         String cname = className;
    834 
    835         atAssignCore(expr, op, right, aType, aDim, cname);
    836 
    837         if (doDup)
    838             if (is2word(aType, aDim))
    839                 bytecode.addOpcode(DUP2_X2);
    840             else
    841                 bytecode.addOpcode(DUP_X2);
    842 
    843         bytecode.addOpcode(getArrayWriteOp(aType, aDim));
    844         exprType = aType;
    845         arrayDim = aDim;
    846         className = cname;
    847     }
    848 
    849     protected abstract void atFieldAssign(Expr expr, int op, ASTree left,
    850                         ASTree right, boolean doDup) throws CompileError;
    851 
    852     protected void atAssignCore(Expr expr, int op, ASTree right,
    853                                 int type, int dim, String cname)
    854         throws CompileError
    855     {
    856         if (op == PLUS_E && dim == 0 && type == CLASS)
    857             atStringPlusEq(expr, type, dim, cname, right);
    858         else {
    859             right.accept(this);
    860             if (invalidDim(exprType, arrayDim, className, type, dim, cname,
    861                            false) || (op != '=' && dim > 0))
    862                 badAssign(expr);
    863 
    864             if (op != '=') {
    865                 int token = assignOps[op - MOD_E];
    866                 int k = lookupBinOp(token);
    867                 if (k < 0)
    868                     fatal();
    869 
    870                 atArithBinExpr(expr, token, k, type);
    871             }
    872         }
    873 
    874         if (op != '=' || (dim == 0 && !isRefType(type)))
    875             atNumCastExpr(exprType, type);
    876 
    877         // type check should be done here.
    878     }
    879 
    880     private void atStringPlusEq(Expr expr, int type, int dim, String cname,
    881                                 ASTree right)
    882         throws CompileError
    883     {
    884         if (!jvmJavaLangString.equals(cname))
    885             badAssign(expr);
    886 
    887         convToString(type, dim);    // the value might be null.
    888         right.accept(this);
    889         convToString(exprType, arrayDim);
    890         bytecode.addInvokevirtual(javaLangString, "concat",
    891                                 "(Ljava/lang/String;)Ljava/lang/String;");
    892         exprType = CLASS;
    893         arrayDim = 0;
    894         className = jvmJavaLangString;
    895     }
    896 
    897     private boolean invalidDim(int srcType, int srcDim, String srcClass,
    898                                int destType, int destDim, String destClass,
    899                                boolean isCast)
    900     {
    901         if (srcDim != destDim)
    902             if (srcType == NULL)
    903                 return false;
    904             else if (destDim == 0 && destType == CLASS
    905                      && jvmJavaLangObject.equals(destClass))
    906                 return false;
    907             else if (isCast && srcDim == 0 && srcType == CLASS
    908                      && jvmJavaLangObject.equals(srcClass))
    909                 return false;
    910             else
    911                 return true;
    912 
    913         return false;
    914     }
    915 
    916     public void atCondExpr(CondExpr expr) throws CompileError {
    917         booleanExpr(false, expr.condExpr());
    918         int pc = bytecode.currentPc();
    919         bytecode.addIndex(0);   // correct later
    920         expr.thenExpr().accept(this);
    921         int dim1 = arrayDim;
    922         bytecode.addOpcode(Opcode.GOTO);
    923         int pc2 = bytecode.currentPc();
    924         bytecode.addIndex(0);
    925         bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
    926         expr.elseExpr().accept(this);
    927         if (dim1 != arrayDim)
    928             throw new CompileError("type mismatch in ?:");
    929 
    930         bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
    931     }
    932 
    933     static final int[] binOp = {
    934         '+', DADD, FADD, LADD, IADD,
    935         '-', DSUB, FSUB, LSUB, ISUB,
    936         '*', DMUL, FMUL, LMUL, IMUL,
    937         '/', DDIV, FDIV, LDIV, IDIV,
    938         '%', DREM, FREM, LREM, IREM,
    939         '|', NOP,  NOP,  LOR,  IOR,
    940         '^', NOP,  NOP,  LXOR, IXOR,
    941         '&', NOP,  NOP,  LAND, IAND,
    942         LSHIFT, NOP, NOP, LSHL, ISHL,
    943         RSHIFT, NOP, NOP, LSHR, ISHR,
    944         ARSHIFT, NOP, NOP, LUSHR, IUSHR };
    945 
    946     static int lookupBinOp(int token) {
    947         int[] code = binOp;
    948         int s = code.length;
    949         for (int k = 0; k < s; k = k + 5)
    950             if (code[k] == token)
    951                 return k;
    952 
    953         return -1;
    954     }
    955 
    956     public void atBinExpr(BinExpr expr) throws CompileError {
    957         int token = expr.getOperator();
    958 
    959         /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
    960          */
    961         int k = lookupBinOp(token);
    962         if (k >= 0) {
    963             expr.oprand1().accept(this);
    964             ASTree right = expr.oprand2();
    965             if (right == null)
    966                 return;     // see TypeChecker.atBinExpr().
    967 
    968             int type1 = exprType;
    969             int dim1 = arrayDim;
    970             String cname1 = className;
    971             right.accept(this);
    972             if (dim1 != arrayDim)
    973                 throw new CompileError("incompatible array types");
    974 
    975             if (token == '+' && dim1 == 0
    976                 && (type1 == CLASS || exprType == CLASS))
    977                 atStringConcatExpr(expr, type1, dim1, cname1);
    978             else
    979                 atArithBinExpr(expr, token, k, type1);
    980         }
    981         else {
    982             /* equation: &&, ||, ==, !=, <=, >=, <, >
    983             */
    984             booleanExpr(true, expr);
    985             bytecode.addIndex(7);
    986             bytecode.addIconst(0);  // false
    987             bytecode.addOpcode(Opcode.GOTO);
    988             bytecode.addIndex(4);
    989             bytecode.addIconst(1);  // true
    990         }
    991     }
    992 
    993     /* arrayDim values of the two oprands must be equal.
    994      * If an oprand type is not a numeric type, this method
    995      * throws an exception.
    996      */
    997     private void atArithBinExpr(Expr expr, int token,
    998                                 int index, int type1) throws CompileError
    999     {
   1000         if (arrayDim != 0)
   1001             badTypes(expr);
   1002 
   1003         int type2 = exprType;
   1004         if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
   1005             if (type2 == INT || type2 == SHORT
   1006                 || type2 == CHAR || type2 == BYTE)
   1007                 exprType = type1;
   1008             else
   1009                 badTypes(expr);
   1010         else
   1011             convertOprandTypes(type1, type2, expr);
   1012 
   1013         int p = typePrecedence(exprType);
   1014         if (p >= 0) {
   1015             int op = binOp[index + p + 1];
   1016             if (op != NOP) {
   1017                 if (p == P_INT && exprType != BOOLEAN)
   1018                     exprType = INT;     // type1 may be BYTE, ...
   1019 
   1020                 bytecode.addOpcode(op);
   1021                 return;
   1022             }
   1023         }
   1024 
   1025         badTypes(expr);
   1026     }
   1027 
   1028     private void atStringConcatExpr(Expr expr, int type1, int dim1,
   1029                                     String cname1) throws CompileError
   1030     {
   1031         int type2 = exprType;
   1032         int dim2 = arrayDim;
   1033         boolean type2Is2 = is2word(type2, dim2);
   1034         boolean type2IsString
   1035             = (type2 == CLASS && jvmJavaLangString.equals(className));
   1036 
   1037         if (type2Is2)
   1038             convToString(type2, dim2);
   1039 
   1040         if (is2word(type1, dim1)) {
   1041             bytecode.addOpcode(DUP_X2);
   1042             bytecode.addOpcode(POP);
   1043         }
   1044         else
   1045             bytecode.addOpcode(SWAP);
   1046 
   1047         // even if type1 is String, the left operand might be null.
   1048         convToString(type1, dim1);
   1049         bytecode.addOpcode(SWAP);
   1050 
   1051         if (!type2Is2 && !type2IsString)
   1052             convToString(type2, dim2);
   1053 
   1054         bytecode.addInvokevirtual(javaLangString, "concat",
   1055                                 "(Ljava/lang/String;)Ljava/lang/String;");
   1056         exprType = CLASS;
   1057         arrayDim = 0;
   1058         className = jvmJavaLangString;
   1059     }
   1060 
   1061     private void convToString(int type, int dim) throws CompileError {
   1062         final String method = "valueOf";
   1063 
   1064         if (isRefType(type) || dim > 0)
   1065             bytecode.addInvokestatic(javaLangString, method,
   1066                                 "(Ljava/lang/Object;)Ljava/lang/String;");
   1067         else if (type == DOUBLE)
   1068             bytecode.addInvokestatic(javaLangString, method,
   1069                                      "(D)Ljava/lang/String;");
   1070         else if (type == FLOAT)
   1071             bytecode.addInvokestatic(javaLangString, method,
   1072                                      "(F)Ljava/lang/String;");
   1073         else if (type == LONG)
   1074             bytecode.addInvokestatic(javaLangString, method,
   1075                                      "(J)Ljava/lang/String;");
   1076         else if (type == BOOLEAN)
   1077             bytecode.addInvokestatic(javaLangString, method,
   1078                                      "(Z)Ljava/lang/String;");
   1079         else if (type == CHAR)
   1080             bytecode.addInvokestatic(javaLangString, method,
   1081                                      "(C)Ljava/lang/String;");
   1082         else if (type == VOID)
   1083             throw new CompileError("void type expression");
   1084         else /* INT, BYTE, SHORT */
   1085             bytecode.addInvokestatic(javaLangString, method,
   1086                                      "(I)Ljava/lang/String;");
   1087     }
   1088 
   1089     /* Produces the opcode to branch if the condition is true.
   1090      * The oprand is not produced.
   1091      *
   1092      * @return	true if the compiled code is GOTO (always branch).
   1093      */
   1094     private boolean booleanExpr(boolean branchIf, ASTree expr)
   1095         throws CompileError
   1096     {
   1097         boolean isAndAnd;
   1098         int op = getCompOperator(expr);
   1099         if (op == EQ) {         // ==, !=, ...
   1100             BinExpr bexpr = (BinExpr)expr;
   1101             int type1 = compileOprands(bexpr);
   1102             // here, arrayDim might represent the array dim. of the left oprand
   1103             // if the right oprand is NULL.
   1104             compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
   1105         }
   1106         else if (op == '!')
   1107             booleanExpr(!branchIf, ((Expr)expr).oprand1());
   1108         else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
   1109             BinExpr bexpr = (BinExpr)expr;
   1110             booleanExpr(!isAndAnd, bexpr.oprand1());
   1111             int pc = bytecode.currentPc();
   1112             bytecode.addIndex(0);       // correct later
   1113 
   1114             booleanExpr(isAndAnd, bexpr.oprand2());
   1115             bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
   1116             if (branchIf != isAndAnd) {
   1117                 bytecode.addIndex(6);   // skip GOTO instruction
   1118                 bytecode.addOpcode(Opcode.GOTO);
   1119             }
   1120         }
   1121         else if (isAlwaysBranch(expr, branchIf)) {
   1122             bytecode.addOpcode(Opcode.GOTO);
   1123             return true;	// always branch
   1124         }
   1125         else {                          // others
   1126             expr.accept(this);
   1127             if (exprType != BOOLEAN || arrayDim != 0)
   1128                 throw new CompileError("boolean expr is required");
   1129 
   1130             bytecode.addOpcode(branchIf ? IFNE : IFEQ);
   1131         }
   1132 
   1133         exprType = BOOLEAN;
   1134         arrayDim = 0;
   1135         return false;
   1136     }
   1137 
   1138 
   1139     private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) {
   1140         if (expr instanceof Keyword) {
   1141             int t = ((Keyword)expr).get();
   1142             return branchIf ? t == TRUE : t == FALSE;
   1143         }
   1144 
   1145         return false;
   1146     }
   1147 
   1148     static int getCompOperator(ASTree expr) throws CompileError {
   1149         if (expr instanceof Expr) {
   1150             Expr bexpr = (Expr)expr;
   1151             int token = bexpr.getOperator();
   1152             if (token == '!')
   1153                 return '!';
   1154             else if ((bexpr instanceof BinExpr)
   1155                      && token != OROR && token != ANDAND
   1156                      && token != '&' && token != '|')
   1157                 return EQ;      // ==, !=, ...
   1158             else
   1159                 return token;
   1160         }
   1161 
   1162         return ' ';     // others
   1163     }
   1164 
   1165     private int compileOprands(BinExpr expr) throws CompileError {
   1166         expr.oprand1().accept(this);
   1167         int type1 = exprType;
   1168         int dim1 = arrayDim;
   1169         expr.oprand2().accept(this);
   1170         if (dim1 != arrayDim)
   1171             if (type1 != NULL && exprType != NULL)
   1172                 throw new CompileError("incompatible array types");
   1173             else if (exprType == NULL)
   1174                 arrayDim = dim1;
   1175 
   1176         if (type1 == NULL)
   1177             return exprType;
   1178         else
   1179             return type1;
   1180     }
   1181 
   1182     private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE,
   1183                                         NEQ, IF_ICMPNE, IF_ICMPEQ,
   1184                                         LE, IF_ICMPLE, IF_ICMPGT,
   1185                                         GE, IF_ICMPGE, IF_ICMPLT,
   1186                                         '<', IF_ICMPLT, IF_ICMPGE,
   1187                                         '>', IF_ICMPGT, IF_ICMPLE };
   1188 
   1189     private static final int ifOp2[] = { EQ, IFEQ, IFNE,
   1190                                          NEQ, IFNE, IFEQ,
   1191                                          LE, IFLE, IFGT,
   1192                                          GE, IFGE, IFLT,
   1193                                          '<', IFLT, IFGE,
   1194                                          '>', IFGT, IFLE };
   1195 
   1196     /* Produces the opcode to branch if the condition is true.
   1197      * The oprands are not produced.
   1198      *
   1199      * Parameter expr - compare expression ==, !=, <=, >=, <, >
   1200      */
   1201     private void compareExpr(boolean branchIf,
   1202                              int token, int type1, BinExpr expr)
   1203         throws CompileError
   1204     {
   1205         if (arrayDim == 0)
   1206             convertOprandTypes(type1, exprType, expr);
   1207 
   1208         int p = typePrecedence(exprType);
   1209         if (p == P_OTHER || arrayDim > 0)
   1210             if (token == EQ)
   1211                 bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE);
   1212             else if (token == NEQ)
   1213                 bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ);
   1214             else
   1215                 badTypes(expr);
   1216         else
   1217             if (p == P_INT) {
   1218                 int op[] = ifOp;
   1219                 for (int i = 0; i < op.length; i += 3)
   1220                     if (op[i] == token) {
   1221                         bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
   1222                         return;
   1223                     }
   1224 
   1225                 badTypes(expr);
   1226             }
   1227             else {
   1228                 if (p == P_DOUBLE)
   1229                     if (token == '<' || token == LE)
   1230                         bytecode.addOpcode(DCMPG);
   1231                     else
   1232                         bytecode.addOpcode(DCMPL);
   1233                 else if (p == P_FLOAT)
   1234                     if (token == '<' || token == LE)
   1235                         bytecode.addOpcode(FCMPG);
   1236                     else
   1237                         bytecode.addOpcode(FCMPL);
   1238                 else if (p == P_LONG)
   1239                     bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: <
   1240                 else
   1241                     fatal();
   1242 
   1243                 int[] op = ifOp2;
   1244                 for (int i = 0; i < op.length; i += 3)
   1245                     if (op[i] == token) {
   1246                         bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
   1247                         return;
   1248                     }
   1249 
   1250                 badTypes(expr);
   1251             }
   1252     }
   1253 
   1254     protected static void badTypes(Expr expr) throws CompileError {
   1255         throw new CompileError("invalid types for " + expr.getName());
   1256     }
   1257 
   1258     private static final int P_DOUBLE = 0;
   1259     private static final int P_FLOAT = 1;
   1260     private static final int P_LONG = 2;
   1261     private static final int P_INT = 3;
   1262     private static final int P_OTHER = -1;
   1263 
   1264     protected static boolean isRefType(int type) {
   1265         return type == CLASS || type == NULL;
   1266     }
   1267 
   1268     private static int typePrecedence(int type) {
   1269         if (type == DOUBLE)
   1270             return P_DOUBLE;
   1271         else if (type == FLOAT)
   1272             return P_FLOAT;
   1273         else if (type == LONG)
   1274             return P_LONG;
   1275         else if (isRefType(type))
   1276             return P_OTHER;
   1277         else if (type == VOID)
   1278             return P_OTHER;     // this is wrong, but ...
   1279         else
   1280             return P_INT;       // BOOLEAN, BYTE, CHAR, SHORT, INT
   1281     }
   1282 
   1283     // used in TypeChecker.
   1284     static boolean isP_INT(int type) {
   1285         return typePrecedence(type) == P_INT;
   1286     }
   1287 
   1288     // used in TypeChecker.
   1289     static boolean rightIsStrong(int type1, int type2) {
   1290         int type1_p = typePrecedence(type1);
   1291         int type2_p = typePrecedence(type2);
   1292         return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p;
   1293     }
   1294 
   1295     private static final int[] castOp = {
   1296             /*            D    F    L    I */
   1297             /* double */ NOP, D2F, D2L, D2I,
   1298             /* float  */ F2D, NOP, F2L, F2I,
   1299             /* long   */ L2D, L2F, NOP, L2I,
   1300             /* other  */ I2D, I2F, I2L, NOP };
   1301 
   1302     /* do implicit type conversion.
   1303      * arrayDim values of the two oprands must be zero.
   1304      */
   1305     private void convertOprandTypes(int type1, int type2, Expr expr)
   1306         throws CompileError
   1307     {
   1308         boolean rightStrong;
   1309         int type1_p = typePrecedence(type1);
   1310         int type2_p = typePrecedence(type2);
   1311 
   1312         if (type2_p < 0 && type1_p < 0) // not primitive types
   1313             return;
   1314 
   1315         if (type2_p < 0 || type1_p < 0) // either is not a primitive type
   1316             badTypes(expr);
   1317 
   1318         int op, result_type;
   1319         if (type1_p <= type2_p) {
   1320             rightStrong = false;
   1321             exprType = type1;
   1322             op = castOp[type2_p * 4 + type1_p];
   1323             result_type = type1_p;
   1324         }
   1325         else {
   1326             rightStrong = true;
   1327             op = castOp[type1_p * 4 + type2_p];
   1328             result_type = type2_p;
   1329         }
   1330 
   1331         if (rightStrong) {
   1332             if (result_type == P_DOUBLE || result_type == P_LONG) {
   1333                 if (type1_p == P_DOUBLE || type1_p == P_LONG)
   1334                     bytecode.addOpcode(DUP2_X2);
   1335                 else
   1336                     bytecode.addOpcode(DUP2_X1);
   1337 
   1338                 bytecode.addOpcode(POP2);
   1339                 bytecode.addOpcode(op);
   1340                 bytecode.addOpcode(DUP2_X2);
   1341                 bytecode.addOpcode(POP2);
   1342             }
   1343             else if (result_type == P_FLOAT) {
   1344                 if (type1_p == P_LONG) {
   1345                     bytecode.addOpcode(DUP_X2);
   1346                     bytecode.addOpcode(POP);
   1347                 }
   1348                 else
   1349                     bytecode.addOpcode(SWAP);
   1350 
   1351                 bytecode.addOpcode(op);
   1352                 bytecode.addOpcode(SWAP);
   1353             }
   1354             else
   1355                 fatal();
   1356         }
   1357         else if (op != NOP)
   1358             bytecode.addOpcode(op);
   1359     }
   1360 
   1361     public void atCastExpr(CastExpr expr) throws CompileError {
   1362         String cname = resolveClassName(expr.getClassName());
   1363         String toClass = checkCastExpr(expr, cname);
   1364         int srcType = exprType;
   1365         exprType = expr.getType();
   1366         arrayDim = expr.getArrayDim();
   1367         className = cname;
   1368         if (toClass == null)
   1369             atNumCastExpr(srcType, exprType);   // built-in type
   1370         else
   1371             bytecode.addCheckcast(toClass);
   1372     }
   1373 
   1374     public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
   1375         String cname = resolveClassName(expr.getClassName());
   1376         String toClass = checkCastExpr(expr, cname);
   1377         bytecode.addInstanceof(toClass);
   1378         exprType = BOOLEAN;
   1379         arrayDim = 0;
   1380     }
   1381 
   1382     private String checkCastExpr(CastExpr expr, String name)
   1383         throws CompileError
   1384     {
   1385         final String msg = "invalid cast";
   1386         ASTree oprand = expr.getOprand();
   1387         int dim = expr.getArrayDim();
   1388         int type = expr.getType();
   1389         oprand.accept(this);
   1390         int srcType = exprType;
   1391         if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
   1392             || srcType == VOID || type == VOID)
   1393             throw new CompileError(msg);
   1394 
   1395         if (type == CLASS) {
   1396             if (!isRefType(srcType))
   1397                 throw new CompileError(msg);
   1398 
   1399             return toJvmArrayName(name, dim);
   1400         }
   1401         else
   1402             if (dim > 0)
   1403                 return toJvmTypeName(type, dim);
   1404             else
   1405                 return null;    // built-in type
   1406     }
   1407 
   1408     void atNumCastExpr(int srcType, int destType)
   1409         throws CompileError
   1410     {
   1411         if (srcType == destType)
   1412             return;
   1413 
   1414         int op, op2;
   1415         int stype = typePrecedence(srcType);
   1416         int dtype = typePrecedence(destType);
   1417         if (0 <= stype && stype < 3)
   1418             op = castOp[stype * 4 + dtype];
   1419         else
   1420             op = NOP;
   1421 
   1422         if (destType == DOUBLE)
   1423             op2 = I2D;
   1424         else if (destType == FLOAT)
   1425             op2 = I2F;
   1426         else if (destType == LONG)
   1427             op2 = I2L;
   1428         else if (destType == SHORT)
   1429             op2 = I2S;
   1430         else if (destType == CHAR)
   1431             op2 = I2C;
   1432         else if (destType == BYTE)
   1433             op2 = I2B;
   1434         else
   1435             op2 = NOP;
   1436 
   1437         if (op != NOP)
   1438             bytecode.addOpcode(op);
   1439 
   1440         if (op == NOP || op == L2I || op == F2I || op == D2I)
   1441             if (op2 != NOP)
   1442                 bytecode.addOpcode(op2);
   1443     }
   1444 
   1445     public void atExpr(Expr expr) throws CompileError {
   1446         // array access, member access,
   1447         // (unary) +, (unary) -, ++, --, !, ~
   1448 
   1449         int token = expr.getOperator();
   1450         ASTree oprand = expr.oprand1();
   1451         if (token == '.') {
   1452             String member = ((Symbol)expr.oprand2()).get();
   1453             if (member.equals("class"))
   1454                 atClassObject(expr);  // .class
   1455             else
   1456                 atFieldRead(expr);
   1457         }
   1458         else if (token == MEMBER) {     // field read
   1459             /* MEMBER ('#') is an extension by Javassist.
   1460              * The compiler internally uses # for compiling .class
   1461              * expressions such as "int.class".
   1462              */
   1463             atFieldRead(expr);
   1464         }
   1465         else if (token == ARRAY)
   1466             atArrayRead(oprand, expr.oprand2());
   1467         else if (token == PLUSPLUS || token == MINUSMINUS)
   1468             atPlusPlus(token, oprand, expr, true);
   1469         else if (token == '!') {
   1470             booleanExpr(false, expr);
   1471             bytecode.addIndex(7);
   1472             bytecode.addIconst(1);
   1473             bytecode.addOpcode(Opcode.GOTO);
   1474             bytecode.addIndex(4);
   1475             bytecode.addIconst(0);
   1476         }
   1477         else if (token == CALL)         // method call
   1478             fatal();
   1479         else {
   1480             expr.oprand1().accept(this);
   1481             int type = typePrecedence(exprType);
   1482             if (arrayDim > 0)
   1483                 badType(expr);
   1484 
   1485             if (token == '-') {
   1486                 if (type == P_DOUBLE)
   1487                     bytecode.addOpcode(DNEG);
   1488                 else if (type == P_FLOAT)
   1489                     bytecode.addOpcode(FNEG);
   1490                 else if (type == P_LONG)
   1491                     bytecode.addOpcode(LNEG);
   1492                 else if (type == P_INT) {
   1493                     bytecode.addOpcode(INEG);
   1494                     exprType = INT;     // type may be BYTE, ...
   1495                 }
   1496                 else
   1497                     badType(expr);
   1498             }
   1499             else if (token == '~') {
   1500                 if (type == P_INT) {
   1501                     bytecode.addIconst(-1);
   1502                     bytecode.addOpcode(IXOR);
   1503                     exprType = INT;     // type may be BYTE. ...
   1504                 }
   1505                 else if (type == P_LONG) {
   1506                     bytecode.addLconst(-1);
   1507                     bytecode.addOpcode(LXOR);
   1508                 }
   1509                 else
   1510                     badType(expr);
   1511 
   1512             }
   1513             else if (token == '+') {
   1514                 if (type == P_OTHER)
   1515                     badType(expr);
   1516 
   1517                 // do nothing. ignore.
   1518             }
   1519             else
   1520                 fatal();
   1521         }
   1522     }
   1523 
   1524     protected static void badType(Expr expr) throws CompileError {
   1525         throw new CompileError("invalid type for " + expr.getName());
   1526     }
   1527 
   1528     public abstract void atCallExpr(CallExpr expr) throws CompileError;
   1529 
   1530     protected abstract void atFieldRead(ASTree expr) throws CompileError;
   1531 
   1532     public void atClassObject(Expr expr) throws CompileError {
   1533         ASTree op1 = expr.oprand1();
   1534         if (!(op1 instanceof Symbol))
   1535             throw new CompileError("fatal error: badly parsed .class expr");
   1536 
   1537         String cname = ((Symbol)op1).get();
   1538         if (cname.startsWith("[")) {
   1539             int i = cname.indexOf("[L");
   1540             if (i >= 0) {
   1541                 String name = cname.substring(i + 2, cname.length() - 1);
   1542                 String name2 = resolveClassName(name);
   1543                 if (!name.equals(name2)) {
   1544                     /* For example, to obtain String[].class,
   1545                      * "[Ljava.lang.String;" (not "[Ljava/lang/String"!)
   1546                      * must be passed to Class.forName().
   1547                      */
   1548                     name2 = MemberResolver.jvmToJavaName(name2);
   1549                     StringBuffer sbuf = new StringBuffer();
   1550                     while (i-- >= 0)
   1551                         sbuf.append('[');
   1552 
   1553                     sbuf.append('L').append(name2).append(';');
   1554                     cname = sbuf.toString();
   1555                 }
   1556             }
   1557         }
   1558         else {
   1559             cname = resolveClassName(MemberResolver.javaToJvmName(cname));
   1560             cname = MemberResolver.jvmToJavaName(cname);
   1561         }
   1562 
   1563         atClassObject2(cname);
   1564         exprType = CLASS;
   1565         arrayDim = 0;
   1566         className = "java/lang/Class";
   1567     }
   1568 
   1569     /* MemberCodeGen overrides this method.
   1570      */
   1571     protected void atClassObject2(String cname) throws CompileError {
   1572         int start = bytecode.currentPc();
   1573         bytecode.addLdc(cname);
   1574         bytecode.addInvokestatic("java.lang.Class", "forName",
   1575                                  "(Ljava/lang/String;)Ljava/lang/Class;");
   1576         int end = bytecode.currentPc();
   1577         bytecode.addOpcode(Opcode.GOTO);
   1578         int pc = bytecode.currentPc();
   1579         bytecode.addIndex(0);   // correct later
   1580 
   1581         bytecode.addExceptionHandler(start, end, bytecode.currentPc(),
   1582                                      "java.lang.ClassNotFoundException");
   1583 
   1584         /* -- the following code is for inlining a call to DotClass.fail().
   1585 
   1586         int var = getMaxLocals();
   1587         incMaxLocals(1);
   1588         bytecode.growStack(1);
   1589         bytecode.addAstore(var);
   1590 
   1591         bytecode.addNew("java.lang.NoClassDefFoundError");
   1592         bytecode.addOpcode(DUP);
   1593         bytecode.addAload(var);
   1594         bytecode.addInvokevirtual("java.lang.ClassNotFoundException",
   1595                                   "getMessage", "()Ljava/lang/String;");
   1596         bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "<init>",
   1597                                   "(Ljava/lang/String;)V");
   1598         */
   1599 
   1600         bytecode.growStack(1);
   1601         bytecode.addInvokestatic("javassist.runtime.DotClass", "fail",
   1602                                  "(Ljava/lang/ClassNotFoundException;)"
   1603                                  + "Ljava/lang/NoClassDefFoundError;");
   1604         bytecode.addOpcode(ATHROW);
   1605         bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
   1606     }
   1607 
   1608     public void atArrayRead(ASTree array, ASTree index)
   1609         throws CompileError
   1610     {
   1611         arrayAccess(array, index);
   1612         bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
   1613     }
   1614 
   1615     protected void arrayAccess(ASTree array, ASTree index)
   1616         throws CompileError
   1617     {
   1618         array.accept(this);
   1619         int type = exprType;
   1620         int dim = arrayDim;
   1621         if (dim == 0)
   1622             throw new CompileError("bad array access");
   1623 
   1624         String cname = className;
   1625 
   1626         index.accept(this);
   1627         if (typePrecedence(exprType) != P_INT || arrayDim > 0)
   1628             throw new CompileError("bad array index");
   1629 
   1630         exprType = type;
   1631         arrayDim = dim - 1;
   1632         className = cname;
   1633     }
   1634 
   1635     protected static int getArrayReadOp(int type, int dim) {
   1636         if (dim > 0)
   1637             return AALOAD;
   1638 
   1639         switch (type) {
   1640         case DOUBLE :
   1641             return DALOAD;
   1642         case FLOAT :
   1643             return FALOAD;
   1644         case LONG :
   1645             return LALOAD;
   1646         case INT :
   1647             return IALOAD;
   1648         case SHORT :
   1649             return SALOAD;
   1650         case CHAR :
   1651             return CALOAD;
   1652         case BYTE :
   1653         case BOOLEAN :
   1654             return BALOAD;
   1655         default :
   1656             return AALOAD;
   1657         }
   1658     }
   1659 
   1660     protected static int getArrayWriteOp(int type, int dim) {
   1661         if (dim > 0)
   1662             return AASTORE;
   1663 
   1664         switch (type) {
   1665         case DOUBLE :
   1666             return DASTORE;
   1667         case FLOAT :
   1668             return FASTORE;
   1669         case LONG :
   1670             return LASTORE;
   1671         case INT :
   1672             return IASTORE;
   1673         case SHORT :
   1674             return SASTORE;
   1675         case CHAR :
   1676             return CASTORE;
   1677         case BYTE :
   1678         case BOOLEAN :
   1679             return BASTORE;
   1680         default :
   1681             return AASTORE;
   1682         }
   1683     }
   1684 
   1685     private void atPlusPlus(int token, ASTree oprand, Expr expr,
   1686                             boolean doDup) throws CompileError
   1687     {
   1688         boolean isPost = oprand == null;        // ++i or i++?
   1689         if (isPost)
   1690             oprand = expr.oprand2();
   1691 
   1692         if (oprand instanceof Variable) {
   1693             Declarator d = ((Variable)oprand).getDeclarator();
   1694             int t = exprType = d.getType();
   1695             arrayDim = d.getArrayDim();
   1696             int var = getLocalVar(d);
   1697             if (arrayDim > 0)
   1698                 badType(expr);
   1699 
   1700             if (t == DOUBLE) {
   1701                 bytecode.addDload(var);
   1702                 if (doDup && isPost)
   1703                     bytecode.addOpcode(DUP2);
   1704 
   1705                 bytecode.addDconst(1.0);
   1706                 bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
   1707                 if (doDup && !isPost)
   1708                     bytecode.addOpcode(DUP2);
   1709 
   1710                 bytecode.addDstore(var);
   1711             }
   1712             else if (t == LONG) {
   1713                 bytecode.addLload(var);
   1714                 if (doDup && isPost)
   1715                     bytecode.addOpcode(DUP2);
   1716 
   1717                 bytecode.addLconst((long)1);
   1718                 bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
   1719                 if (doDup && !isPost)
   1720                     bytecode.addOpcode(DUP2);
   1721 
   1722                 bytecode.addLstore(var);
   1723             }
   1724             else if (t == FLOAT) {
   1725                 bytecode.addFload(var);
   1726                 if (doDup && isPost)
   1727                     bytecode.addOpcode(DUP);
   1728 
   1729                 bytecode.addFconst(1.0f);
   1730                 bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
   1731                 if (doDup && !isPost)
   1732                     bytecode.addOpcode(DUP);
   1733 
   1734                 bytecode.addFstore(var);
   1735             }
   1736             else if (t == BYTE || t == CHAR || t == SHORT || t == INT) {
   1737                 if (doDup && isPost)
   1738                     bytecode.addIload(var);
   1739 
   1740                 int delta = token == PLUSPLUS ? 1 : -1;
   1741                 if (var > 0xff) {
   1742                     bytecode.addOpcode(WIDE);
   1743                     bytecode.addOpcode(IINC);
   1744                     bytecode.addIndex(var);
   1745                     bytecode.addIndex(delta);
   1746                 }
   1747                 else {
   1748                     bytecode.addOpcode(IINC);
   1749                     bytecode.add(var);
   1750                     bytecode.add(delta);
   1751                 }
   1752 
   1753                 if (doDup && !isPost)
   1754                     bytecode.addIload(var);
   1755             }
   1756             else
   1757                 badType(expr);
   1758         }
   1759         else {
   1760             if (oprand instanceof Expr) {
   1761                 Expr e = (Expr)oprand;
   1762                 if (e.getOperator() == ARRAY) {
   1763                     atArrayPlusPlus(token, isPost, e, doDup);
   1764                     return;
   1765                 }
   1766             }
   1767 
   1768             atFieldPlusPlus(token, isPost, oprand, expr, doDup);
   1769         }
   1770     }
   1771 
   1772     public void atArrayPlusPlus(int token, boolean isPost,
   1773                         Expr expr, boolean doDup) throws CompileError
   1774     {
   1775         arrayAccess(expr.oprand1(), expr.oprand2());
   1776         int t = exprType;
   1777         int dim = arrayDim;
   1778         if (dim > 0)
   1779             badType(expr);
   1780 
   1781         bytecode.addOpcode(DUP2);
   1782         bytecode.addOpcode(getArrayReadOp(t, arrayDim));
   1783         int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
   1784         atPlusPlusCore(dup_code, doDup, token, isPost, expr);
   1785         bytecode.addOpcode(getArrayWriteOp(t, dim));
   1786     }
   1787 
   1788     protected void atPlusPlusCore(int dup_code, boolean doDup,
   1789                                   int token, boolean isPost,
   1790                                   Expr expr) throws CompileError
   1791     {
   1792         int t = exprType;
   1793 
   1794         if (doDup && isPost)
   1795             bytecode.addOpcode(dup_code);
   1796 
   1797         if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
   1798             bytecode.addIconst(1);
   1799             bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
   1800             exprType = INT;
   1801         }
   1802         else if (t == LONG) {
   1803             bytecode.addLconst((long)1);
   1804             bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
   1805         }
   1806         else if (t == FLOAT) {
   1807             bytecode.addFconst(1.0f);
   1808             bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
   1809         }
   1810         else if (t == DOUBLE) {
   1811             bytecode.addDconst(1.0);
   1812             bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
   1813         }
   1814         else
   1815             badType(expr);
   1816 
   1817         if (doDup && !isPost)
   1818             bytecode.addOpcode(dup_code);
   1819     }
   1820 
   1821     protected abstract void atFieldPlusPlus(int token, boolean isPost,
   1822                 ASTree oprand, Expr expr, boolean doDup) throws CompileError;
   1823 
   1824     public abstract void atMember(Member n) throws CompileError;
   1825 
   1826     public void atVariable(Variable v) throws CompileError {
   1827         Declarator d = v.getDeclarator();
   1828         exprType = d.getType();
   1829         arrayDim = d.getArrayDim();
   1830         className = d.getClassName();
   1831         int var = getLocalVar(d);
   1832 
   1833         if (arrayDim > 0)
   1834             bytecode.addAload(var);
   1835         else
   1836             switch (exprType) {
   1837             case CLASS :
   1838                 bytecode.addAload(var);
   1839                 break;
   1840             case LONG :
   1841                 bytecode.addLload(var);
   1842                 break;
   1843             case FLOAT :
   1844                 bytecode.addFload(var);
   1845                 break;
   1846             case DOUBLE :
   1847                 bytecode.addDload(var);
   1848                 break;
   1849             default :   // BOOLEAN, BYTE, CHAR, SHORT, INT
   1850                 bytecode.addIload(var);
   1851                 break;
   1852             }
   1853     }
   1854 
   1855     public void atKeyword(Keyword k) throws CompileError {
   1856         arrayDim = 0;
   1857         int token = k.get();
   1858         switch (token) {
   1859         case TRUE :
   1860             bytecode.addIconst(1);
   1861             exprType = BOOLEAN;
   1862             break;
   1863         case FALSE :
   1864             bytecode.addIconst(0);
   1865             exprType = BOOLEAN;
   1866             break;
   1867         case NULL :
   1868             bytecode.addOpcode(ACONST_NULL);
   1869             exprType = NULL;
   1870             break;
   1871         case THIS :
   1872         case SUPER :
   1873             if (inStaticMethod)
   1874                 throw new CompileError("not-available: "
   1875                                        + (token == THIS ? "this" : "super"));
   1876 
   1877             bytecode.addAload(0);
   1878             exprType = CLASS;
   1879             if (token == THIS)
   1880                 className = getThisName();
   1881             else
   1882                 className = getSuperName();
   1883             break;
   1884         default :
   1885             fatal();
   1886         }
   1887     }
   1888 
   1889     public void atStringL(StringL s) throws CompileError {
   1890         exprType = CLASS;
   1891         arrayDim = 0;
   1892         className = jvmJavaLangString;
   1893         bytecode.addLdc(s.get());
   1894     }
   1895 
   1896     public void atIntConst(IntConst i) throws CompileError {
   1897         arrayDim = 0;
   1898         long value = i.get();
   1899         int type = i.getType();
   1900         if (type == IntConstant || type == CharConstant) {
   1901             exprType = (type == IntConstant ? INT : CHAR);
   1902             bytecode.addIconst((int)value);
   1903         }
   1904         else {
   1905             exprType = LONG;
   1906             bytecode.addLconst(value);
   1907         }
   1908     }
   1909 
   1910     public void atDoubleConst(DoubleConst d) throws CompileError {
   1911         arrayDim = 0;
   1912         if (d.getType() == DoubleConstant) {
   1913             exprType = DOUBLE;
   1914             bytecode.addDconst(d.get());
   1915         }
   1916         else {
   1917             exprType = FLOAT;
   1918             bytecode.addFconst((float)d.get());
   1919         }
   1920     }
   1921 }
   1922