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
16package javassist.compiler;
17
18import java.util.ArrayList;
19import java.util.Arrays;
20import javassist.compiler.ast.*;
21import 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
28public 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