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