• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.compiler;
17 
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import javassist.compiler.ast.*;
21 import javassist.bytecode.*;
22 
23 /* The code generator is implemeted by three files:
24  * CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
25  * I just wanted to split a big file into three smaller ones.
26  */
27 
28 public abstract class CodeGen extends Visitor implements Opcode, TokenId {
29     static final String javaLangObject = "java.lang.Object";
30     static final String jvmJavaLangObject = "java/lang/Object";
31 
32     static final String javaLangString = "java.lang.String";
33     static final String jvmJavaLangString = "java/lang/String";
34 
35     protected Bytecode bytecode;
36     private int tempVar;
37     TypeChecker typeChecker;
38 
39     /**
40      * true if the last visited node is a return statement.
41      */
42     protected boolean hasReturned;
43 
44     /**
45      * Must be true if compilation is for a static method.
46      */
47     public boolean inStaticMethod;
48 
49     protected ArrayList breakList, continueList;
50 
51     /**
52      * doit() in ReturnHook is called from atReturn().
53      */
54     protected static abstract class ReturnHook {
55         ReturnHook next;
56 
57         /**
58          * Returns true if the generated code ends with return,
59          * throw, or goto.
60          */
doit(Bytecode b, int opcode)61         protected abstract boolean doit(Bytecode b, int opcode);
62 
ReturnHook(CodeGen gen)63         protected ReturnHook(CodeGen gen) {
64             next = gen.returnHooks;
65             gen.returnHooks = this;
66         }
67 
remove(CodeGen gen)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 
CodeGen(Bytecode b)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 
setTypeChecker(TypeChecker checker)93     public void setTypeChecker(TypeChecker checker) {
94         typeChecker = checker;
95     }
96 
fatal()97     protected static void fatal() throws CompileError {
98         throw new CompileError("fatal");
99     }
100 
is2word(int type, int dim)101     public static boolean is2word(int type, int dim) {
102         return dim == 0 && (type == DOUBLE || type == LONG);
103     }
104 
getMaxLocals()105     public int getMaxLocals() { return bytecode.getMaxLocals(); }
106 
setMaxLocals(int n)107     public void setMaxLocals(int n) {
108         bytecode.setMaxLocals(n);
109     }
110 
incMaxLocals(int size)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      */
getTempVar()119     protected int getTempVar() {
120         if (tempVar < 0) {
121             tempVar = getMaxLocals();
122             incMaxLocals(2);
123         }
124 
125         return tempVar;
126     }
127 
getLocalVar(Declarator d)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      */
getThisName()142     protected abstract String getThisName();
143 
144     /**
145      * Returns the JVM-internal representation of this super class name.
146      */
getSuperName()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      */
resolveClassName(ASTList name)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      */
resolveClassName(String jvmClassName)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      */
toJvmArrayName(String name, int dim)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 
toJvmTypeName(int type, int dim)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 
compileExpr(ASTree expr)227     public void compileExpr(ASTree expr) throws CompileError {
228         doTypeCheck(expr);
229         expr.accept(this);
230     }
231 
compileBooleanExpr(boolean branchIf, ASTree expr)232     public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
233         throws CompileError
234     {
235         doTypeCheck(expr);
236         return booleanExpr(branchIf, expr);
237     }
238 
doTypeCheck(ASTree expr)239     public void doTypeCheck(ASTree expr) throws CompileError {
240         if (typeChecker != null)
241             expr.accept(typeChecker);
242     }
243 
atASTList(ASTList n)244     public void atASTList(ASTList n) throws CompileError { fatal(); }
245 
atPair(Pair n)246     public void atPair(Pair n) throws CompileError { fatal(); }
247 
atSymbol(Symbol n)248     public void atSymbol(Symbol n) throws CompileError { fatal(); }
249 
atFieldDecl(FieldDecl field)250     public void atFieldDecl(FieldDecl field) throws CompileError {
251         field.getInit().accept(this);
252     }
253 
atMethodDecl(MethodDecl method)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      */
atMethodBody(Stmnt s, boolean isCons, boolean isVoid)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 
needsSuperCall(Stmnt body)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 
insertDefaultSuperCall()320     protected abstract void insertDefaultSuperCall() throws CompileError;
321 
atStmnt(Stmnt st)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 
atIfStmnt(Stmnt st)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 
atWhileStmnt(Stmnt st, boolean notDo)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 
patchGoto(ArrayList list, int targetPc)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 
atForStmnt(Stmnt st)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 
atSwitchStmnt(Stmnt st)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 
computeLabel(ASTree expr)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 
atBreakStmnt(Stmnt st, boolean notCont)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 
atReturnStmnt(Stmnt st)588     protected void atReturnStmnt(Stmnt st) throws CompileError {
589         atReturnStmnt2(st.getLeft());
590     }
591 
atReturnStmnt2(ASTree result)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 
atThrowStmnt(Stmnt st)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      */
atTryStmnt(Stmnt st)637     protected void atTryStmnt(Stmnt st) throws CompileError {
638         hasReturned = false;
639     }
640 
atSyncStmnt(Stmnt st)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 
getListSize(ArrayList list)696     private static int getListSize(ArrayList list) {
697         return list == null ? 0 : list.size();
698     }
699 
isPlusPlusExpr(ASTree expr)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 
atDeclarator(Declarator d)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 
atNewExpr(NewExpr n)730     public abstract void atNewExpr(NewExpr n) throws CompileError;
731 
atArrayInit(ArrayInit init)732     public abstract void atArrayInit(ArrayInit init) throws CompileError;
733 
atAssignExpr(AssignExpr expr)734     public void atAssignExpr(AssignExpr expr) throws CompileError {
735         atAssignExpr(expr, true);
736     }
737 
atAssignExpr(AssignExpr expr, boolean doDup)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 
badAssign(Expr expr)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      */
atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right, boolean doDup)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 
atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass)818     protected abstract void atArrayVariableAssign(ArrayInit init,
819             int varType, int varArray, String varClass) throws CompileError;
820 
atArrayAssign(Expr expr, int op, Expr array, ASTree right, boolean doDup)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 
atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup)849     protected abstract void atFieldAssign(Expr expr, int op, ASTree left,
850                         ASTree right, boolean doDup) throws CompileError;
851 
atAssignCore(Expr expr, int op, ASTree right, int type, int dim, String cname)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 
atStringPlusEq(Expr expr, int type, int dim, String cname, ASTree right)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 
invalidDim(int srcType, int srcDim, String srcClass, int destType, int destDim, String destClass, boolean isCast)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 
atCondExpr(CondExpr expr)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 
lookupBinOp(int token)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 
atBinExpr(BinExpr expr)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      */
atArithBinExpr(Expr expr, int token, int index, int type1)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 
atStringConcatExpr(Expr expr, int type1, int dim1, String cname1)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 
convToString(int type, int dim)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      */
booleanExpr(boolean branchIf, ASTree expr)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 
isAlwaysBranch(ASTree expr, boolean branchIf)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 
getCompOperator(ASTree expr)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 
compileOprands(BinExpr expr)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      */
compareExpr(boolean branchIf, int token, int type1, BinExpr expr)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 
badTypes(Expr expr)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 
isRefType(int type)1264     protected static boolean isRefType(int type) {
1265         return type == CLASS || type == NULL;
1266     }
1267 
typePrecedence(int type)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.
isP_INT(int type)1284     static boolean isP_INT(int type) {
1285         return typePrecedence(type) == P_INT;
1286     }
1287 
1288     // used in TypeChecker.
rightIsStrong(int type1, int type2)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      */
convertOprandTypes(int type1, int type2, Expr expr)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 
atCastExpr(CastExpr expr)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 
atInstanceOfExpr(InstanceOfExpr expr)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 
checkCastExpr(CastExpr expr, String name)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 
atNumCastExpr(int srcType, int destType)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 
atExpr(Expr expr)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 
badType(Expr expr)1524     protected static void badType(Expr expr) throws CompileError {
1525         throw new CompileError("invalid type for " + expr.getName());
1526     }
1527 
atCallExpr(CallExpr expr)1528     public abstract void atCallExpr(CallExpr expr) throws CompileError;
1529 
atFieldRead(ASTree expr)1530     protected abstract void atFieldRead(ASTree expr) throws CompileError;
1531 
atClassObject(Expr expr)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      */
atClassObject2(String cname)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 
atArrayRead(ASTree array, ASTree index)1608     public void atArrayRead(ASTree array, ASTree index)
1609         throws CompileError
1610     {
1611         arrayAccess(array, index);
1612         bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
1613     }
1614 
arrayAccess(ASTree array, ASTree index)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 
getArrayReadOp(int type, int dim)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 
getArrayWriteOp(int type, int dim)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 
atPlusPlus(int token, ASTree oprand, Expr expr, boolean doDup)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 
atArrayPlusPlus(int token, boolean isPost, Expr expr, boolean doDup)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 
atPlusPlusCore(int dup_code, boolean doDup, int token, boolean isPost, Expr expr)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 
atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup)1821     protected abstract void atFieldPlusPlus(int token, boolean isPost,
1822                 ASTree oprand, Expr expr, boolean doDup) throws CompileError;
1823 
atMember(Member n)1824     public abstract void atMember(Member n) throws CompileError;
1825 
atVariable(Variable v)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 
atKeyword(Keyword k)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 
atStringL(StringL s)1889     public void atStringL(StringL s) throws CompileError {
1890         exprType = CLASS;
1891         arrayDim = 0;
1892         className = jvmJavaLangString;
1893         bytecode.addLdc(s.get());
1894     }
1895 
atIntConst(IntConst i)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 
atDoubleConst(DoubleConst d)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