KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javassist > compiler > CodeGen


1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2005 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 JavaDoc;
19 import java.util.Arrays JavaDoc;
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 JavaDoc javaLangObject = "java.lang.Object";
30     static final String JavaDoc jvmJavaLangObject = "java/lang/Object";
31
32     static final String JavaDoc javaLangString = "java.lang.String";
33     static final String JavaDoc 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 JavaDoc breakList, continueList;
50
51     /**
52      * doit() in ReturnHook is called from atReturn().
53      */

54     protected static abstract class ReturnHook {
55         ReturnHook next;
56         protected abstract void doit(Bytecode b, int opcode);
57         protected ReturnHook(CodeGen gen) {
58             next = gen.returnHooks;
59             gen.returnHooks = this;
60         }
61
62         protected void remove(CodeGen gen) {
63             gen.returnHooks = next;
64         }
65     }
66
67     protected ReturnHook returnHooks;
68
69     /* The following fields are used by atXXX() methods
70      * for returning the type of the compiled expression.
71      */

72     protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
73
protected int arrayDim;
74     protected String JavaDoc className; // JVM-internal representation
75

76     public CodeGen(Bytecode b) {
77         bytecode = b;
78         tempVar = -1;
79         typeChecker = null;
80         hasReturned = false;
81         inStaticMethod = false;
82         breakList = null;
83         continueList = null;
84         returnHooks = null;
85     }
86
87     public void setTypeChecker(TypeChecker checker) {
88         typeChecker = checker;
89     }
90
91     protected static void fatal() throws CompileError {
92         throw new CompileError("fatal");
93     }
94
95     public static boolean is2word(int type, int dim) {
96         return dim == 0 && (type == DOUBLE || type == LONG);
97     }
98
99     public int getMaxLocals() { return bytecode.getMaxLocals(); }
100
101     public void setMaxLocals(int n) {
102         bytecode.setMaxLocals(n);
103     }
104
105     protected void incMaxLocals(int size) {
106         bytecode.incMaxLocals(size);
107     }
108
109     /**
110      * Returns a local variable that single or double words can be
111      * stored in.
112      */

113     protected int getTempVar() {
114         if (tempVar < 0) {
115             tempVar = getMaxLocals();
116             incMaxLocals(2);
117         }
118
119         return tempVar;
120     }
121
122     protected int getLocalVar(Declarator d) {
123         int v = d.getLocalVar();
124         if (v < 0) {
125             v = getMaxLocals(); // delayed variable allocation.
126
d.setLocalVar(v);
127             incMaxLocals(1);
128         }
129
130         return v;
131     }
132
133     /**
134      * Returns the JVM-internal representation of this class name.
135      */

136     protected abstract String JavaDoc getThisName();
137
138     /**
139      * Returns the JVM-internal representation of this super class name.
140      */

141     protected abstract String JavaDoc getSuperName() throws CompileError;
142
143     /* Converts a class name into a JVM-internal representation.
144      *
145      * It may also expand a simple class name to java.lang.*.
146      * For example, this converts Object into java/lang/Object.
147      */

148     protected abstract String JavaDoc resolveClassName(ASTList name)
149         throws CompileError;
150
151     /* Expands a simple class name to java.lang.*.
152      * For example, this converts Object into java/lang/Object.
153      */

154     protected abstract String JavaDoc resolveClassName(String JavaDoc jvmClassName)
155         throws CompileError;
156
157     /**
158      * @param name the JVM-internal representation.
159      * name is not exapnded to java.lang.*.
160      */

161     protected static String JavaDoc toJvmArrayName(String JavaDoc name, int dim) {
162         if (name == null)
163             return null;
164
165         if (dim == 0)
166             return name;
167         else {
168             StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
169             int d = dim;
170             while (d-- > 0)
171                 sbuf.append('[');
172
173             sbuf.append('L');
174             sbuf.append(name);
175             sbuf.append(';');
176
177             return sbuf.toString();
178         }
179     }
180
181     protected static String JavaDoc toJvmTypeName(int type, int dim) {
182         char c = 'I';
183         switch(type) {
184         case BOOLEAN :
185             c = 'Z';
186             break;
187         case BYTE :
188             c = 'B';
189             break;
190         case CHAR :
191             c = 'C';
192             break;
193         case SHORT :
194             c = 'S';
195             break;
196         case INT :
197             c = 'I';
198             break;
199         case LONG :
200             c = 'J';
201             break;
202         case FLOAT :
203             c = 'F';
204             break;
205         case DOUBLE :
206             c = 'D';
207             break;
208         case VOID :
209             c = 'V';
210             break;
211         }
212
213         StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
214         while (dim-- > 0)
215                 sbuf.append('[');
216
217         sbuf.append(c);
218         return sbuf.toString();
219     }
220
221     public void compileExpr(ASTree expr) throws CompileError {
222         doTypeCheck(expr);
223         expr.accept(this);
224     }
225
226     public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
227         throws CompileError
228     {
229         doTypeCheck(expr);
230         return booleanExpr(branchIf, expr);
231     }
232
233     public void doTypeCheck(ASTree expr) throws CompileError {
234         if (typeChecker != null)
235             expr.accept(typeChecker);
236     }
237
238     public void atASTList(ASTList n) throws CompileError { fatal(); }
239     
240     public void atPair(Pair n) throws CompileError { fatal(); }
241
242     public void atSymbol(Symbol n) throws CompileError { fatal(); }
243
244     public void atFieldDecl(FieldDecl field) throws CompileError {
245         field.getInit().accept(this);
246     }
247
248     public void atMethodDecl(MethodDecl method) throws CompileError {
249         ASTList mods = method.getModifiers();
250         setMaxLocals(1);
251         while (mods != null) {
252             Keyword k = (Keyword)mods.head();
253             mods = mods.tail();
254             if (k.get() == STATIC) {
255                 setMaxLocals(0);
256                 inStaticMethod = true;
257             }
258         }
259             
260         ASTList params = method.getParams();
261         while (params != null) {
262             atDeclarator((Declarator)params.head());
263             params = params.tail();
264         }
265
266         Stmnt s = method.getBody();
267         atMethodBody(s, method.isConstructor(),
268                      method.getReturn().getType() == VOID);
269     }
270
271     /**
272      * @param isCons true if super() must be called.
273      * false if the method is a class initializer.
274      */

275     public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid)
276         throws CompileError
277     {
278         if (s == null)
279             return;
280
281         if (isCons && needsSuperCall(s))
282             insertDefaultSuperCall();
283
284         hasReturned = false;
285         s.accept(this);
286         if (!hasReturned)
287             if (isVoid) {
288                 bytecode.addOpcode(Opcode.RETURN);
289                 hasReturned = true;
290             }
291             else
292                 throw new CompileError("no return statement");
293     }
294
295     private boolean needsSuperCall(Stmnt body) throws CompileError {
296         if (body.getOperator() == BLOCK)
297             body = (Stmnt)body.head();
298
299         if (body != null && body.getOperator() == EXPR) {
300             ASTree expr = body.head();
301             if (expr != null && expr instanceof Expr
302                 && ((Expr)expr).getOperator() == CALL) {
303                 ASTree target = ((Expr)expr).head();
304                 if (target instanceof Keyword) {
305                     int token = ((Keyword)target).get();
306                     return token != THIS && token != SUPER;
307                 }
308             }
309         }
310
311         return true;
312     }
313
314     protected abstract void insertDefaultSuperCall() throws CompileError;
315
316     public void atStmnt(Stmnt st) throws CompileError {
317         if (st == null)
318             return; // empty
319

320         int op = st.getOperator();
321         if (op == EXPR) {
322             ASTree expr = st.getLeft();
323             doTypeCheck(expr);
324             if (expr instanceof AssignExpr)
325                 atAssignExpr((AssignExpr)expr, false);
326             else if (isPlusPlusExpr(expr)) {
327                 Expr e = (Expr)expr;
328                 atPlusPlus(e.getOperator(), e.oprand1(), e, false);
329             }
330             else {
331                 expr.accept(this);
332                 if (is2word(exprType, arrayDim))
333                     bytecode.addOpcode(POP2);
334                 else if (exprType != VOID)
335                     bytecode.addOpcode(POP);
336             }
337         }
338         else if (op == DECL || op == BLOCK) {
339             ASTList list = st;
340             while (list != null) {
341                 ASTree h = list.head();
342                 list = list.tail();
343                 if (h != null)
344                     h.accept(this);
345             }
346         }
347         else if (op == IF)
348             atIfStmnt(st);
349         else if (op == WHILE || op == DO)
350             atWhileStmnt(st, op == WHILE);
351         else if (op == FOR)
352             atForStmnt(st);
353         else if (op == BREAK || op == CONTINUE)
354             atBreakStmnt(st, op == BREAK);
355         else if (op == TokenId.RETURN)
356             atReturnStmnt(st);
357         else if (op == THROW)
358             atThrowStmnt(st);
359         else if (op == TRY)
360             atTryStmnt(st);
361         else if (op == SWITCH)
362             atSwitchStmnt(st);
363         else if (op == SYNCHRONIZED)
364             atSyncStmnt(st);
365         else {
366             // LABEL, SWITCH label stament might be null?.
367
hasReturned = false;
368             throw new CompileError(
369                 "sorry, not supported statement: TokenId " + op);
370         }
371     }
372
373     private void atIfStmnt(Stmnt st) throws CompileError {
374         ASTree expr = st.head();
375         Stmnt thenp = (Stmnt)st.tail().head();
376         Stmnt elsep = (Stmnt)st.tail().tail().head();
377         compileBooleanExpr(false, expr);
378         int pc = bytecode.currentPc();
379         int pc2 = 0;
380         bytecode.addIndex(0); // correct later
381

382         hasReturned = false;
383         if (thenp != null)
384             thenp.accept(this);
385
386         boolean thenHasReturned = hasReturned;
387         hasReturned = false;
388
389         if (elsep != null && !thenHasReturned) {
390             bytecode.addOpcode(Opcode.GOTO);
391             pc2 = bytecode.currentPc();
392             bytecode.addIndex(0);
393         }
394
395         bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
396
397         if (elsep != null) {
398             elsep.accept(this);
399             if (!thenHasReturned)
400                 bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
401
402             hasReturned = thenHasReturned && hasReturned;
403         }
404     }
405
406     private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
407         ArrayList JavaDoc prevBreakList = breakList;
408         ArrayList JavaDoc prevContList = continueList;
409         breakList = new ArrayList JavaDoc();
410         continueList = new ArrayList JavaDoc();
411
412         ASTree expr = st.head();
413         Stmnt body = (Stmnt)st.tail();
414
415         int pc = 0;
416         if (notDo) {
417             bytecode.addOpcode(Opcode.GOTO);
418             pc = bytecode.currentPc();
419             bytecode.addIndex(0);
420         }
421
422         int pc2 = bytecode.currentPc();
423         if (body != null)
424             body.accept(this);
425
426         int pc3 = bytecode.currentPc();
427         if (notDo)
428             bytecode.write16bit(pc, pc3 - pc + 1);
429
430         boolean alwaysBranch = compileBooleanExpr(true, expr);
431         bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
432
433         patchGoto(breakList, bytecode.currentPc());
434         patchGoto(continueList, pc3);
435         continueList = prevContList;
436         breakList = prevBreakList;
437         hasReturned = alwaysBranch;
438     }
439
440     protected void patchGoto(ArrayList JavaDoc list, int targetPc) {
441         int n = list.size();
442         for (int i = 0; i < n; ++i) {
443             int pc = ((Integer JavaDoc)list.get(i)).intValue();
444             bytecode.write16bit(pc, targetPc - pc + 1);
445         }
446     }
447
448     private void atForStmnt(Stmnt st) throws CompileError {
449         ArrayList JavaDoc prevBreakList = breakList;
450         ArrayList JavaDoc prevContList = continueList;
451         breakList = new ArrayList JavaDoc();
452         continueList = new ArrayList JavaDoc();
453
454         Stmnt init = (Stmnt)st.head();
455         ASTList p = st.tail();
456         ASTree expr = p.head();
457         p = p.tail();
458         Stmnt update = (Stmnt)p.head();
459         Stmnt body = (Stmnt)p.tail();
460
461         if (init != null)
462             init.accept(this);
463
464         int pc = bytecode.currentPc();
465         int pc2 = 0;
466         if (expr != null) {
467             compileBooleanExpr(false, expr);
468             pc2 = bytecode.currentPc();
469             bytecode.addIndex(0);
470         }
471
472         if (body != null)
473             body.accept(this);
474
475         int pc3 = bytecode.currentPc();
476         if (update != null)
477             update.accept(this);
478
479         bytecode.addOpcode(Opcode.GOTO);
480         bytecode.addIndex(pc - bytecode.currentPc() + 1);
481
482         int pc4 = bytecode.currentPc();
483         if (expr != null)
484             bytecode.write16bit(pc2, pc4 - pc2 + 1);
485
486         patchGoto(breakList, pc4);
487         patchGoto(continueList, pc3);
488         continueList = prevContList;
489         breakList = prevBreakList;
490         hasReturned = false;
491     }
492
493     private void atSwitchStmnt(Stmnt st) throws CompileError {
494         compileExpr(st.head());
495
496         ArrayList JavaDoc prevBreakList = breakList;
497         breakList = new ArrayList JavaDoc();
498         int opcodePc = bytecode.currentPc();
499         bytecode.addOpcode(LOOKUPSWITCH);
500         int npads = 3 - (opcodePc & 3);
501         while (npads-- > 0)
502             bytecode.add(0);
503
504         Stmnt body = (Stmnt)st.tail();
505         int npairs = 0;
506         for (ASTList list = body; list != null; list = list.tail())
507             if (((Stmnt)list.head()).getOperator() == CASE)
508                 ++npairs;
509
510         // opcodePc2 is the position at which the default jump offset is.
511
int opcodePc2 = bytecode.currentPc();
512         bytecode.addGap(4);
513         bytecode.add32bit(npairs);
514         bytecode.addGap(npairs * 8);
515
516         long[] pairs = new long[npairs];
517         int ipairs = 0;
518         int defaultPc = -1;
519         for (ASTList list = body; list != null; list = list.tail()) {
520             Stmnt label = (Stmnt)list.head();
521             int op = label.getOperator();
522             if (op == DEFAULT)
523                 defaultPc = bytecode.currentPc();
524             else if (op != CASE)
525                 fatal();
526             else {
527                 pairs[ipairs++]
528                     = ((long)computeLabel(label.head()) << 32) +
529                       ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff);
530             }
531
532             hasReturned = false;
533             ((Stmnt)label.tail()).accept(this);
534         }
535
536         Arrays.sort(pairs);
537         int pc = opcodePc2 + 8;
538         for (int i = 0; i < npairs; ++i) {
539             bytecode.write32bit(pc, (int)(pairs[i] >>> 32));
540             bytecode.write32bit(pc + 4, (int)pairs[i]);
541             pc += 8;
542         }
543
544         if (defaultPc < 0 || breakList.size() > 0)
545             hasReturned = false;
546
547         int endPc = bytecode.currentPc();
548         if (defaultPc < 0)
549             defaultPc = endPc;
550
551         bytecode.write32bit(opcodePc2, defaultPc - opcodePc);
552
553         patchGoto(breakList, endPc);
554         breakList = prevBreakList;
555     }
556
557     private int computeLabel(ASTree expr) throws CompileError {
558         doTypeCheck(expr);
559         expr = TypeChecker.stripPlusExpr(expr);
560         if (expr instanceof IntConst)
561             return (int)((IntConst)expr).get();
562         else
563             throw new CompileError("bad case label");
564     }
565
566     private void atBreakStmnt(Stmnt st, boolean notCont)
567         throws CompileError
568     {
569         if (st.head() != null)
570             throw new CompileError(
571                         "sorry, not support labeled break or continue");
572
573         bytecode.addOpcode(Opcode.GOTO);
574         Integer JavaDoc pc = new Integer JavaDoc(bytecode.currentPc());
575         bytecode.addIndex(0);
576         if (notCont)
577             breakList.add(pc);
578         else
579             continueList.add(pc);
580     }
581
582     protected void atReturnStmnt(Stmnt st) throws CompileError {
583         atReturnStmnt2(st.getLeft());
584     }
585
586     protected final void atReturnStmnt2(ASTree result) throws CompileError {
587         int op;
588         if (result == null)
589             op = Opcode.RETURN;
590         else {
591             compileExpr(result);
592             if (arrayDim > 0)
593                 op = ARETURN;
594             else {
595                 int type = exprType;
596                 if (type == DOUBLE)
597                     op = DRETURN;
598                 else if (type == FLOAT)
599                     op = FRETURN;
600                 else if (type == LONG)
601                     op = LRETURN;
602                 else if (isRefType(type))
603                     op = ARETURN;
604                 else
605                     op = IRETURN;
606             }
607         }
608
609         for (ReturnHook har = returnHooks; har != null; har = har.next)
610             har.doit(bytecode, op);
611
612         bytecode.addOpcode(op);
613         hasReturned = true;
614     }
615
616     private void atThrowStmnt(Stmnt st) throws CompileError {
617         ASTree e = st.getLeft();
618         compileExpr(e);
619         if (exprType != CLASS || arrayDim > 0)
620             throw new CompileError("bad throw statement");
621
622         bytecode.addOpcode(ATHROW);
623         hasReturned = true;
624     }
625
626     /* overridden in MemberCodeGen
627      */

628     protected void atTryStmnt(Stmnt st) throws CompileError {
629         hasReturned = false;
630     }
631
632     private void atSyncStmnt(Stmnt st) throws CompileError {
633         int nbreaks = getListSize(breakList);
634         int ncontinues = getListSize(continueList);
635
636         compileExpr(st.head());
637         if (exprType != CLASS && arrayDim == 0)
638             throw new CompileError("bad type expr for synchronized block");
639
640         Bytecode bc = bytecode;
641         final int var = bc.getMaxLocals();
642         bc.incMaxLocals(1);
643         bc.addOpcode(DUP);
644         bc.addAstore(var);
645         bc.addOpcode(MONITORENTER);
646
647         ReturnHook rh = new ReturnHook(this) {
648             protected void doit(Bytecode b, int opcode) {
649                 b.addAload(var);
650                 b.addOpcode(MONITOREXIT);
651             }
652         };
653
654         int pc = bc.currentPc();
655         Stmnt body = (Stmnt)st.tail();
656         if (body != null)
657             body.accept(this);
658
659         int pc2 = bc.currentPc();
660         int pc3 = 0;
661         if (!hasReturned) {
662             rh.doit(bc, 0); // the 2nd arg is ignored.
663
bc.addOpcode(Opcode.GOTO);
664             pc3 = bc.currentPc();
665             bc.addIndex(0);
666         }
667
668         int pc4 = bc.currentPc();
669         rh.doit(bc, 0); // the 2nd arg is ignored.
670
bc.addOpcode(ATHROW);
671         bc.addExceptionHandler(pc, pc2, pc4, 0);
672         if (!hasReturned)
673             bc.write16bit(pc3, bc.currentPc() - pc3 + 1);
674
675         rh.remove(this);
676
677         if (getListSize(breakList) != nbreaks
678             || getListSize(continueList) != ncontinues)
679             throw new CompileError(
680                 "sorry, cannot break/continue in synchronized block");
681     }
682
683     private static int getListSize(ArrayList JavaDoc list) {
684         return list == null ? 0 : list.size();
685     }
686
687     private static boolean isPlusPlusExpr(ASTree expr) {
688         if (expr instanceof Expr) {
689             int op = ((Expr)expr).getOperator();
690             return op == PLUSPLUS || op == MINUSMINUS;
691         }
692
693         return false;
694     }
695
696     public void atDeclarator(Declarator d) throws CompileError {
697         d.setLocalVar(getMaxLocals());
698         d.setClassName(resolveClassName(d.getClassName()));
699
700         int size;
701         if (is2word(d.getType(), d.getArrayDim()))
702             size = 2;
703         else
704             size = 1;
705
706         incMaxLocals(size);
707
708         /* NOTE: Array initializers has not been supported.
709          */

710         ASTree init = d.getInitializer();
711         if (init != null) {
712             doTypeCheck(init);
713             atVariableAssign(null, '=', null, d, init, false);
714         }
715     }
716
717     public abstract void atNewExpr(NewExpr n) throws CompileError;
718
719     public void atAssignExpr(AssignExpr expr) throws CompileError {
720         atAssignExpr(expr, true);
721     }
722
723     protected void atAssignExpr(AssignExpr expr, boolean doDup)
724         throws CompileError
725     {
726         // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
727
int op = expr.getOperator();
728         ASTree left = expr.oprand1();
729         ASTree right = expr.oprand2();
730         if (left instanceof Variable)
731             atVariableAssign(expr, op, (Variable)left,
732                              ((Variable)left).getDeclarator(),
733                              right, doDup);
734         else {
735             if (left instanceof Expr) {
736                 Expr e = (Expr)left;
737                 if (e.getOperator() == ARRAY) {
738                     atArrayAssign(expr, op, (Expr)left, right, doDup);
739                     return;
740                 }
741             }
742
743             atFieldAssign(expr, op, left, right, doDup);
744         }
745     }
746
747     protected static void badAssign(Expr expr) throws CompileError {
748         String JavaDoc msg;
749         if (expr == null)
750             msg = "incompatible type for assignment";
751         else
752             msg = "incompatible type for " + expr.getName();
753
754         throw new CompileError(msg);
755     }
756
757     /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
758      *
759      * expr and var can be null.
760      */

761     private void atVariableAssign(Expr expr, int op, Variable var,
762                                   Declarator d, ASTree right,
763                                   boolean doDup) throws CompileError
764     {
765         int varType = d.getType();
766         int varArray = d.getArrayDim();
767         String JavaDoc varClass = d.getClassName();
768         int varNo = getLocalVar(d);
769
770         if (op != '=')
771             atVariable(var);
772
773         atAssignCore(expr, op, right, varType, varArray, varClass);
774
775         if (doDup)
776             if (is2word(varType, varArray))
777                 bytecode.addOpcode(DUP2);
778             else
779                 bytecode.addOpcode(DUP);
780
781         if (varArray > 0)
782             bytecode.addAstore(varNo);
783         else if (varType == DOUBLE)
784             bytecode.addDstore(varNo);
785         else if (varType == FLOAT)
786             bytecode.addFstore(varNo);
787         else if (varType == LONG)
788             bytecode.addLstore(varNo);
789         else if (isRefType(varType))
790             bytecode.addAstore(varNo);
791         else
792             bytecode.addIstore(varNo);
793
794         exprType = varType;
795         arrayDim = varArray;
796         className = varClass;
797     }
798
799     private void atArrayAssign(Expr expr, int op, Expr array,
800                         ASTree right, boolean doDup) throws CompileError
801     {
802         arrayAccess(array.oprand1(), array.oprand2());
803
804         if (op != '=') {
805             bytecode.addOpcode(DUP2);
806             bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
807         }
808
809         int aType = exprType;
810         int aDim = arrayDim;
811         String JavaDoc cname = className;
812
813         atAssignCore(expr, op, right, aType, aDim, cname);
814
815         if (doDup)
816             if (is2word(aType, aDim))
817                 bytecode.addOpcode(DUP2_X2);
818             else
819                 bytecode.addOpcode(DUP_X2);
820
821         bytecode.addOpcode(getArrayWriteOp(aType, aDim));
822         exprType = aType;
823         arrayDim = aDim;
824         className = cname;
825     }
826
827     protected abstract void atFieldAssign(Expr expr, int op, ASTree left,
828                         ASTree right, boolean doDup) throws CompileError;
829
830     protected void atAssignCore(Expr expr, int op, ASTree right,
831                                 int type, int dim, String JavaDoc cname)
832         throws CompileError
833     {
834         if (op == PLUS_E && dim == 0 && type == CLASS)
835             atStringPlusEq(expr, type, dim, cname, right);
836         else {
837             right.accept(this);
838             if (invalidDim(exprType, arrayDim, className, type, dim, cname,
839                            false) || (op != '=' && dim > 0))
840                 badAssign(expr);
841
842             if (op != '=') {
843                 int token = assignOps[op - MOD_E];
844                 int k = lookupBinOp(token);
845                 if (k < 0)
846                     fatal();
847
848                 atArithBinExpr(expr, token, k, type);
849             }
850         }
851
852         if (op != '=' || (dim == 0 && !isRefType(type)))
853             atNumCastExpr(exprType, type);
854
855         // type check should be done here.
856
}
857
858     private void atStringPlusEq(Expr expr, int type, int dim, String JavaDoc cname,
859                                 ASTree right)
860         throws CompileError
861     {
862         if (!jvmJavaLangString.equals(cname))
863             badAssign(expr);
864
865         convToString(type, dim); // the value might be null.
866
right.accept(this);
867         convToString(exprType, arrayDim);
868         bytecode.addInvokevirtual(javaLangString, "concat",
869                                 "(Ljava/lang/String;)Ljava/lang/String;");
870         exprType = CLASS;
871         arrayDim = 0;
872         className = jvmJavaLangString;
873     }
874
875     private boolean invalidDim(int srcType, int srcDim, String JavaDoc srcClass,
876                                int destType, int destDim, String JavaDoc destClass,
877                                boolean isCast)
878     {
879         if (srcDim != destDim)
880             if (srcType == NULL)
881                 return false;
882             else if (destDim == 0 && destType == CLASS
883                      && jvmJavaLangObject.equals(destClass))
884                 return false;
885             else if (isCast && srcDim == 0 && srcType == CLASS
886                      && jvmJavaLangObject.equals(srcClass))
887                 return false;
888             else
889                 return true;
890
891         return false;
892     }
893
894     public void atCondExpr(CondExpr expr) throws CompileError {
895         booleanExpr(false, expr.condExpr());
896         int pc = bytecode.currentPc();
897         bytecode.addIndex(0); // correct later
898
expr.thenExpr().accept(this);
899         int dim1 = arrayDim;
900         bytecode.addOpcode(Opcode.GOTO);
901         int pc2 = bytecode.currentPc();
902         bytecode.addIndex(0);
903         bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
904         expr.elseExpr().accept(this);
905         if (dim1 != arrayDim)
906             throw new CompileError("type mismatch in ?:");
907
908         bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
909     }
910
911     static final int[] binOp = {
912         '+', DADD, FADD, LADD, IADD,
913         '-', DSUB, FSUB, LSUB, ISUB,
914         '*', DMUL, FMUL, LMUL, IMUL,
915         '/', DDIV, FDIV, LDIV, IDIV,
916         '%', DREM, FREM, LREM, IREM,
917         '|', NOP, NOP, LOR, IOR,
918         '^', NOP, NOP, LXOR, IXOR,
919         '&', NOP, NOP, LAND, IAND,
920         LSHIFT, NOP, NOP, LSHL, ISHL,
921         RSHIFT, NOP, NOP, LSHR, ISHR,
922         ARSHIFT, NOP, NOP, LUSHR, IUSHR };
923
924     static int lookupBinOp(int token) {
925         int[] code = binOp;
926         int s = code.length;
927         for (int k = 0; k < s; k = k + 5)
928             if (code[k] == token)
929                 return k;
930
931         return -1;
932     }
933
934     public void atBinExpr(BinExpr expr) throws CompileError {
935         int token = expr.getOperator();
936
937         /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
938          */

939         int k = lookupBinOp(token);
940         if (k >= 0) {
941             expr.oprand1().accept(this);
942             ASTree right = expr.oprand2();
943             if (right == null)
944                 return; // see TypeChecker.atBinExpr().
945

946             int type1 = exprType;
947             int dim1 = arrayDim;
948             String JavaDoc cname1 = className;
949             right.accept(this);
950             if (dim1 != arrayDim)
951                 throw new CompileError("incompatible array types");
952
953             if (token == '+' && dim1 == 0
954                 && (type1 == CLASS || exprType == CLASS))
955                 atStringConcatExpr(expr, type1, dim1, cname1);
956             else
957                 atArithBinExpr(expr, token, k, type1);
958         }
959         else {
960             /* equation: &&, ||, ==, !=, <=, >=, <, >
961             */

962             booleanExpr(true, expr);
963             bytecode.addIndex(7);
964             bytecode.addIconst(0); // false
965
bytecode.addOpcode(Opcode.GOTO);
966             bytecode.addIndex(4);
967             bytecode.addIconst(1); // true
968
}
969     }
970
971     /* arrayDim values of the two oprands must be equal.
972      * If an oprand type is not a numeric type, this method
973      * throws an exception.
974      */

975     private void atArithBinExpr(Expr expr, int token,
976                                 int index, int type1) throws CompileError
977     {
978         if (arrayDim != 0)
979             badTypes(expr);
980
981         int type2 = exprType;
982         if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
983             if (type2 == INT || type2 == SHORT
984                 || type2 == CHAR || type2 == BYTE)
985                 exprType = type1;
986             else
987                 badTypes(expr);
988         else
989             convertOprandTypes(type1, type2, expr);
990
991         int p = typePrecedence(exprType);
992         if (p >= 0) {
993             int op = binOp[index + p + 1];
994             if (op != NOP) {
995                 if (p == P_INT && exprType != BOOLEAN)
996                     exprType = INT; // type1 may be BYTE, ...
997

998                 bytecode.addOpcode(op);
999                 return;
1000            }
1001        }
1002
1003        badTypes(expr);
1004    }
1005
1006    private void atStringConcatExpr(Expr expr, int type1, int dim1,
1007                                    String JavaDoc cname1) throws CompileError
1008    {
1009        int type2 = exprType;
1010        int dim2 = arrayDim;
1011        boolean type2Is2 = is2word(type2, dim2);
1012        boolean type2IsString
1013            = (type2 == CLASS && jvmJavaLangString.equals(className));
1014
1015        if (type2Is2)
1016            convToString(type2, dim2);
1017
1018        if (is2word(type1, dim1)) {
1019            bytecode.addOpcode(DUP_X2);
1020            bytecode.addOpcode(POP);
1021        }
1022        else
1023            bytecode.addOpcode(SWAP);
1024
1025        // even if type1 is String, the left operand might be null.
1026
convToString(type1, dim1);
1027        bytecode.addOpcode(SWAP);
1028
1029        if (!type2Is2 && !type2IsString)
1030            convToString(type2, dim2);
1031
1032        bytecode.addInvokevirtual(javaLangString, "concat",
1033                                "(Ljava/lang/String;)Ljava/lang/String;");
1034        exprType = CLASS;
1035        arrayDim = 0;
1036        className = jvmJavaLangString;
1037    }
1038
1039    private void convToString(int type, int dim) throws CompileError {
1040        final String JavaDoc method = "valueOf";
1041
1042        if (isRefType(type) || dim > 0)
1043            bytecode.addInvokestatic(javaLangString, method,
1044                                "(Ljava/lang/Object;)Ljava/lang/String;");
1045        else if (type == DOUBLE)
1046            bytecode.addInvokestatic(javaLangString, method,
1047                                     "(D)Ljava/lang/String;");
1048        else if (type == FLOAT)
1049            bytecode.addInvokestatic(javaLangString, method,
1050                                     "(F)Ljava/lang/String;");
1051        else if (type == LONG)
1052            bytecode.addInvokestatic(javaLangString, method,
1053                                     "(J)Ljava/lang/String;");
1054        else if (type == BOOLEAN)
1055            bytecode.addInvokestatic(javaLangString, method,
1056                                     "(Z)Ljava/lang/String;");
1057        else if (type == CHAR)
1058            bytecode.addInvokestatic(javaLangString, method,
1059                                     "(C)Ljava/lang/String;");
1060        else if (type == VOID)
1061            throw new CompileError("void type expression");
1062        else /* INT, BYTE, SHORT */
1063            bytecode.addInvokestatic(javaLangString, method,
1064                                     "(I)Ljava/lang/String;");
1065    }
1066
1067    /* Produces the opcode to branch if the condition is true.
1068     * The oprand is not produced.
1069     *
1070     * @return true if the compiled code is GOTO (always branch).
1071     */

1072    private boolean booleanExpr(boolean branchIf, ASTree expr)
1073        throws CompileError
1074    {
1075        boolean isAndAnd;
1076        int op = getCompOperator(expr);
1077        if (op == EQ) { // ==, !=, ...
1078
BinExpr bexpr = (BinExpr)expr;
1079            int type1 = compileOprands(bexpr);
1080            compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
1081        }
1082        else if (op == '!')
1083            booleanExpr(!branchIf, ((Expr)expr).oprand1());
1084        else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
1085            BinExpr bexpr = (BinExpr)expr;
1086            booleanExpr(!isAndAnd, bexpr.oprand1());
1087            int pc = bytecode.currentPc();
1088            bytecode.addIndex(0); // correct later
1089

1090            booleanExpr(isAndAnd, bexpr.oprand2());
1091            bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
1092            if (branchIf != isAndAnd) {
1093                bytecode.addIndex(6); // skip GOTO instruction
1094
bytecode.addOpcode(Opcode.GOTO);
1095            }
1096        }
1097        else if (isAlwaysBranch(expr, branchIf)) {
1098            bytecode.addOpcode(Opcode.GOTO);
1099            return true; // always branch
1100
}
1101        else { // others
1102
expr.accept(this);
1103            if (exprType != BOOLEAN || arrayDim != 0)
1104                throw new CompileError("boolean expr is required");
1105
1106            bytecode.addOpcode(branchIf ? IFNE : IFEQ);
1107        }
1108
1109        exprType = BOOLEAN;
1110        arrayDim = 0;
1111        return false;
1112    }
1113
1114
1115    private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) {
1116        if (expr instanceof Keyword) {
1117            int t = ((Keyword)expr).get();
1118            return branchIf ? t == TRUE : t == FALSE;
1119        }
1120
1121        return false;
1122    }
1123
1124    static int getCompOperator(ASTree expr) throws CompileError {
1125        if (expr instanceof Expr) {
1126            Expr bexpr = (Expr)expr;
1127            int token = bexpr.getOperator();
1128            if (token == '!')
1129                return '!';
1130            else if ((bexpr instanceof BinExpr)
1131                     && token != OROR && token != ANDAND
1132                     && token != '&' && token != '|')
1133                return EQ; // ==, !=, ...
1134
else
1135                return token;
1136        }
1137
1138        return ' '; // others
1139
}
1140
1141    private int compileOprands(BinExpr expr) throws CompileError {
1142        expr.oprand1().accept(this);
1143        int type1 = exprType;
1144        int dim1 = arrayDim;
1145        expr.oprand2().accept(this);
1146        if (dim1 != arrayDim)
1147            throw new CompileError("incompatible array types");
1148
1149        return type1;
1150    }
1151
1152    private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE,
1153                                        NEQ, IF_ICMPNE, IF_ICMPEQ,
1154                                        LE, IF_ICMPLE, IF_ICMPGT,
1155                                        GE, IF_ICMPGE, IF_ICMPLT,
1156                                        '<', IF_ICMPLT, IF_ICMPGE,
1157                                        '>', IF_ICMPGT, IF_ICMPLE };
1158
1159    private static final int ifOp2[] = { EQ, IFEQ, IFNE,
1160                                         NEQ, IFNE, IFEQ,
1161                                         LE, IFLE, IFGT,
1162                                         GE, IFGE, IFLT,
1163                                         '<', IFLT, IFGE,
1164                                         '>', IFGT, IFLE };
1165
1166    /* Produces the opcode to branch if the condition is true.
1167     * The oprands are not produced.
1168     *
1169     * Parameter expr - compare expression ==, !=, <=, >=, <, >
1170     */

1171    private void compareExpr(boolean branchIf,
1172                             int token, int type1, BinExpr expr)
1173        throws CompileError
1174    {
1175        if (arrayDim == 0)
1176            convertOprandTypes(type1, exprType, expr);
1177
1178        int p = typePrecedence(exprType);
1179        if (p == P_OTHER || arrayDim > 0)
1180            if (token == EQ)
1181                bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE);
1182            else if (token == NEQ)
1183                bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ);
1184            else
1185                badTypes(expr);
1186        else
1187            if (p == P_INT) {
1188                int op[] = ifOp;
1189                for (int i = 0; i < op.length; i += 3)
1190                    if (op[i] == token) {
1191                        bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
1192                        return;
1193                    }
1194
1195                badTypes(expr);
1196            }
1197            else {
1198                if (p == P_DOUBLE)
1199                    if (token == '<' || token == LE)
1200                        bytecode.addOpcode(DCMPG);
1201                    else
1202                        bytecode.addOpcode(DCMPL);
1203                else if (p == P_FLOAT)
1204                    if (token == '<' || token == LE)
1205                        bytecode.addOpcode(FCMPG);
1206                    else
1207                        bytecode.addOpcode(FCMPL);
1208                else if (p == P_LONG)
1209                    bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: <
1210
else
1211                    fatal();
1212
1213                int[] op = ifOp2;
1214                for (int i = 0; i < op.length; i += 3)
1215                    if (op[i] == token) {
1216                        bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
1217                        return;
1218                    }
1219
1220                badTypes(expr);
1221            }
1222    }
1223
1224    protected static void badTypes(Expr expr) throws CompileError {
1225        throw new CompileError("invalid types for " + expr.getName());
1226    }
1227
1228    private static final int P_DOUBLE = 0;
1229    private static final int P_FLOAT = 1;
1230    private static final int P_LONG = 2;
1231    private static final int P_INT = 3;
1232    private static final int P_OTHER = -1;
1233
1234    protected static boolean isRefType(int type) {
1235        return type == CLASS || type == NULL;
1236    }
1237
1238    private static int typePrecedence(int type) {
1239        if (type == DOUBLE)
1240            return P_DOUBLE;
1241        else if (type == FLOAT)
1242            return P_FLOAT;
1243        else if (type == LONG)
1244            return P_LONG;
1245        else if (isRefType(type))
1246            return P_OTHER;
1247        else if (type == VOID)
1248            return P_OTHER; // this is wrong, but ...
1249
else
1250            return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT
1251
}
1252
1253    // used in TypeChecker.
1254
static boolean isP_INT(int type) {
1255        return typePrecedence(type) == P_INT;
1256    }
1257
1258    // used in TypeChecker.
1259
static boolean rightIsStrong(int type1, int type2) {
1260        int type1_p = typePrecedence(type1);
1261        int type2_p = typePrecedence(type2);
1262        return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p;
1263    }
1264
1265    private static final int[] castOp = {
1266            /* D F L I */
1267            /* double */ NOP, D2F, D2L, D2I,
1268            /* float */ F2D, NOP, F2L, F2I,
1269            /* long */ L2D, L2F, NOP, L2I,
1270            /* other */ I2D, I2F, I2L, NOP };
1271
1272    /* do implicit type conversion.
1273     * arrayDim values of the two oprands must be zero.
1274     */

1275    private void convertOprandTypes(int type1, int type2, Expr expr)
1276        throws CompileError
1277    {
1278        boolean rightStrong;
1279        int type1_p = typePrecedence(type1);
1280        int type2_p = typePrecedence(type2);
1281
1282        if (type2_p < 0 && type1_p < 0) // not primitive types
1283
return;
1284
1285        if (type2_p < 0 || type1_p < 0) // either is not a primitive type
1286
badTypes(expr);
1287
1288        int op, result_type;
1289        if (type1_p <= type2_p) {
1290            rightStrong = false;
1291            exprType = type1;
1292            op = castOp[type2_p * 4 + type1_p];
1293            result_type = type1_p;
1294        }
1295        else {
1296            rightStrong = true;
1297            op = castOp[type1_p * 4 + type2_p];
1298            result_type = type2_p;
1299        }
1300
1301        if (rightStrong) {
1302            if (result_type == P_DOUBLE || result_type == P_LONG) {
1303                if (type1_p == P_DOUBLE || type1_p == P_LONG)
1304                    bytecode.addOpcode(DUP2_X2);
1305                else
1306                    bytecode.addOpcode(DUP2_X1);
1307
1308                bytecode.addOpcode(POP2);
1309                bytecode.addOpcode(op);
1310                bytecode.addOpcode(DUP2_X2);
1311                bytecode.addOpcode(POP2);
1312            }
1313            else if (result_type == P_FLOAT) {
1314                if (type1_p == P_LONG) {
1315                    bytecode.addOpcode(DUP_X2);
1316                    bytecode.addOpcode(POP);
1317                }
1318                else
1319                    bytecode.addOpcode(SWAP);
1320
1321                bytecode.addOpcode(op);
1322                bytecode.addOpcode(SWAP);
1323            }
1324            else
1325                fatal();
1326        }
1327        else if (op != NOP)
1328            bytecode.addOpcode(op);
1329    }
1330
1331    public void atCastExpr(CastExpr expr) throws CompileError {
1332        String JavaDoc cname = resolveClassName(expr.getClassName());
1333        String JavaDoc toClass = checkCastExpr(expr, cname);
1334        int srcType = exprType;
1335        exprType = expr.getType();
1336        arrayDim = expr.getArrayDim();
1337        className = cname;
1338        if (toClass == null)
1339            atNumCastExpr(srcType, exprType); // built-in type
1340
else
1341            bytecode.addCheckcast(toClass);
1342    }
1343
1344    public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
1345        String JavaDoc cname = resolveClassName(expr.getClassName());
1346        String JavaDoc toClass = checkCastExpr(expr, cname);
1347        bytecode.addInstanceof(toClass);
1348        exprType = BOOLEAN;
1349        arrayDim = 0;
1350    }
1351
1352    private String JavaDoc checkCastExpr(CastExpr expr, String JavaDoc name)
1353        throws CompileError
1354    {
1355        final String JavaDoc msg = "invalid cast";
1356        ASTree oprand = expr.getOprand();
1357        int dim = expr.getArrayDim();
1358        int type = expr.getType();
1359        oprand.accept(this);
1360        int srcType = exprType;
1361        if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
1362            || srcType == VOID || type == VOID)
1363            throw new CompileError(msg);
1364
1365        if (type == CLASS) {
1366            if (!isRefType(srcType))
1367                throw new CompileError(msg);
1368
1369            return toJvmArrayName(name, dim);
1370        }
1371        else
1372            if (dim > 0)
1373                return toJvmTypeName(type, dim);
1374            else
1375                return null; // built-in type
1376
}
1377
1378    void atNumCastExpr(int srcType, int destType)
1379        throws CompileError
1380    {
1381        if (srcType == destType)
1382            return;
1383        
1384        int op, op2;
1385        int stype = typePrecedence(srcType);
1386        int dtype = typePrecedence(destType);
1387        if (0 <= stype && stype < 3)
1388            op = castOp[stype * 4 + dtype];
1389        else
1390            op = NOP;
1391
1392        if (destType == DOUBLE)
1393            op2 = I2D;
1394        else if (destType == FLOAT)
1395            op2 = I2F;
1396        else if (destType == LONG)
1397            op2 = I2L;
1398        else if (destType == SHORT)
1399            op2 = I2S;
1400        else if (destType == CHAR)
1401            op2 = I2C;
1402        else if (destType == BYTE)
1403            op2 = I2B;
1404        else
1405            op2 = NOP;
1406
1407        if (op != NOP)
1408            bytecode.addOpcode(op);
1409
1410        if (op == NOP || op == L2I || op == F2I || op == D2I)
1411            if (op2 != NOP)
1412                bytecode.addOpcode(op2);
1413    }
1414
1415    public void atExpr(Expr expr) throws CompileError {
1416        // array access, member access,
1417
// (unary) +, (unary) -, ++, --, !, ~
1418

1419        int token = expr.getOperator();
1420        ASTree oprand = expr.oprand1();
1421        if (token == '.') {
1422            String JavaDoc member = ((Symbol)expr.oprand2()).get();
1423            if (member.equals("length"))
1424                atArrayLength(expr);
1425            else if (member.equals("class"))
1426                atClassObject(expr); // .class
1427
else
1428                atFieldRead(expr);
1429        }
1430        else if (token == MEMBER) { // field read
1431
/* MEMBER ('#') is an extension by Javassist.
1432             * The compiler internally uses # for compiling .class
1433             * expressions such as "int.class".
1434             */

1435            atFieldRead(expr);
1436        }
1437        else if (token == ARRAY)
1438            atArrayRead(oprand, expr.oprand2());
1439        else if (token == PLUSPLUS || token == MINUSMINUS)
1440            atPlusPlus(token, oprand, expr, true);
1441        else if (token == '!') {
1442            booleanExpr(false, expr);
1443            bytecode.addIndex(7);
1444            bytecode.addIconst(1);
1445            bytecode.addOpcode(Opcode.GOTO);
1446            bytecode.addIndex(4);
1447            bytecode.addIconst(0);
1448        }
1449        else if (token == CALL) // method call
1450
fatal();
1451        else {
1452            expr.oprand1().accept(this);
1453            int type = typePrecedence(exprType);
1454            if (arrayDim > 0)
1455                badType(expr);
1456
1457            if (token == '-') {
1458                if (type == P_DOUBLE)
1459                    bytecode.addOpcode(DNEG);
1460                else if (type == P_FLOAT)
1461                    bytecode.addOpcode(FNEG);
1462                else if (type == P_LONG)
1463                    bytecode.addOpcode(LNEG);
1464                else if (type == P_INT) {
1465                    bytecode.addOpcode(INEG);
1466                    exprType = INT; // type may be BYTE, ...
1467
}
1468                else
1469                    badType(expr);
1470            }
1471            else if (token == '~') {
1472                if (type == P_INT) {
1473                    bytecode.addIconst(-1);
1474                    bytecode.addOpcode(IXOR);
1475                    exprType = INT; // type may be BYTE. ...
1476
}
1477                else if (type == P_LONG) {
1478                    bytecode.addLconst(-1);
1479                    bytecode.addOpcode(LXOR);
1480                }
1481                else
1482                    badType(expr);
1483
1484            }
1485            else if (token == '+') {
1486                if (type == P_OTHER)
1487                    badType(expr);
1488
1489                // do nothing. ignore.
1490
}
1491            else
1492                fatal();
1493        }
1494    }
1495
1496    protected static void badType(Expr expr) throws CompileError {
1497        throw new CompileError("invalid type for " + expr.getName());
1498    }
1499
1500    public abstract void atCallExpr(CallExpr expr) throws CompileError;
1501
1502    protected abstract void atFieldRead(ASTree expr) throws CompileError;
1503
1504    public void atClassObject(Expr expr) throws CompileError {
1505        ASTree op1 = expr.oprand1();
1506        if (!(op1 instanceof Symbol))
1507            throw new CompileError("fatal error: badly parsed .class expr");
1508
1509        String JavaDoc cname = ((Symbol)op1).get();
1510        if (cname.startsWith("[")) {
1511            int i = cname.indexOf("[L");
1512            if (i >= 0) {
1513                String JavaDoc name = cname.substring(i + 2, cname.length() - 1);
1514                String JavaDoc name2 = resolveClassName(name);
1515                if (!name.equals(name2)) {
1516                    /* For example, to obtain String[].class,
1517                     * "[Ljava.lang.String;" (not "[Ljava/lang/String"!)
1518                     * must be passed to Class.forName().
1519                     */

1520                    name2 = MemberResolver.jvmToJavaName(name2);
1521                    StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
1522                    while (i-- >= 0)
1523                        sbuf.append('[');
1524
1525                    sbuf.append('L').append(name2).append(';');
1526                    cname = sbuf.toString();
1527                }
1528            }
1529        }
1530        else {
1531            cname = resolveClassName(MemberResolver.javaToJvmName(cname));
1532            cname = MemberResolver.jvmToJavaName(cname);
1533        }
1534
1535        int start = bytecode.currentPc();
1536        bytecode.addLdc(cname);
1537        bytecode.addInvokestatic("java.lang.Class", "forName",
1538                                 "(Ljava/lang/String;)Ljava/lang/Class;");
1539        int end = bytecode.currentPc();
1540        bytecode.addOpcode(Opcode.GOTO);
1541        int pc = bytecode.currentPc();
1542        bytecode.addIndex(0); // correct later
1543

1544        bytecode.addExceptionHandler(start, end, bytecode.currentPc(),
1545                                     "java.lang.ClassNotFoundException");
1546
1547        /* -- the following code is for inlining a call to DotClass.fail().
1548
1549        int var = getMaxLocals();
1550        incMaxLocals(1);
1551        bytecode.growStack(1);
1552        bytecode.addAstore(var);
1553
1554        bytecode.addNew("java.lang.NoClassDefFoundError");
1555        bytecode.addOpcode(DUP);
1556        bytecode.addAload(var);
1557        bytecode.addInvokevirtual("java.lang.ClassNotFoundException",
1558                                  "getMessage", "()Ljava/lang/String;");
1559        bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "<init>",
1560                                  "(Ljava/lang/String;)V");
1561        */

1562
1563        bytecode.growStack(1);
1564        bytecode.addInvokestatic("javassist.runtime.DotClass", "fail",
1565                                 "(Ljava/lang/ClassNotFoundException;)"
1566                                 + "Ljava/lang/NoClassDefFoundError;");
1567        bytecode.addOpcode(ATHROW);
1568        bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
1569
1570        exprType = CLASS;
1571        arrayDim = 0;
1572        className = "java/lang/Class";
1573    }
1574
1575    public void atArrayLength(Expr expr) throws CompileError {
1576        expr.oprand1().accept(this);
1577        if (arrayDim == 0)
1578            throw new CompileError(".length applied to a non array");
1579
1580        bytecode.addOpcode(ARRAYLENGTH);
1581        exprType = INT;
1582        arrayDim = 0;
1583    }
1584
1585    public void atArrayRead(ASTree array, ASTree index)
1586        throws CompileError
1587    {
1588        int op;
1589        arrayAccess(array, index);
1590        bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
1591    }
1592
1593    protected void arrayAccess(ASTree array, ASTree index)
1594        throws CompileError
1595    {
1596        array.accept(this);
1597        int type = exprType;
1598        int dim = arrayDim;
1599        if (dim == 0)
1600            throw new CompileError("bad array access");
1601
1602        String JavaDoc cname = className;
1603
1604        index.accept(this);
1605        if (typePrecedence(exprType) != P_INT || arrayDim > 0)
1606            throw new CompileError("bad array index");
1607
1608        exprType = type;
1609        arrayDim = dim - 1;
1610        className = cname;
1611    }
1612
1613    protected static int getArrayReadOp(int type, int dim) {
1614        int op;
1615        if (dim > 0)
1616            return AALOAD;
1617
1618        switch (type) {
1619        case DOUBLE :
1620            return DALOAD;
1621        case FLOAT :
1622            return FALOAD;
1623        case LONG :
1624            return LALOAD;
1625        case INT :
1626            return IALOAD;
1627        case SHORT :
1628            return SALOAD;
1629        case CHAR :
1630            return CALOAD;
1631        case BYTE :
1632        case BOOLEAN :
1633            return BALOAD;
1634        default :
1635            return AALOAD;
1636        }
1637    }
1638
1639    protected static int getArrayWriteOp(int type, int dim) {
1640        int op;
1641        if (dim > 0)
1642            return AASTORE;
1643
1644        switch (type) {
1645        case DOUBLE :
1646            return DASTORE;
1647        case FLOAT :
1648            return FASTORE;
1649        case LONG :
1650            return LASTORE;
1651        case INT :
1652            return IASTORE;
1653        case CHAR :
1654            return CASTORE;
1655        case BYTE :
1656        case BOOLEAN :
1657            return BASTORE;
1658        default :
1659            return AASTORE;
1660        }
1661    }
1662
1663    private void atPlusPlus(int token, ASTree oprand, Expr expr,
1664                            boolean doDup) throws CompileError
1665    {
1666        boolean isPost = oprand == null; // ++i or i++?
1667
if (isPost)
1668            oprand = expr.oprand2();
1669
1670        if (oprand instanceof Variable) {
1671            Declarator d = ((Variable)oprand).getDeclarator();
1672            int t = exprType = d.getType();
1673            arrayDim = d.getArrayDim();
1674            int var = getLocalVar(d);
1675            if (arrayDim > 0)
1676                badType(expr);
1677
1678            if (t == DOUBLE) {
1679                bytecode.addDload(var);
1680                if (doDup && isPost)
1681                    bytecode.addOpcode(DUP2);
1682
1683                bytecode.addDconst(1.0);
1684                bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
1685                if (doDup && !isPost)
1686                    bytecode.addOpcode(DUP2);
1687
1688                bytecode.addDstore(var);
1689            }
1690            else if (t == LONG) {
1691                bytecode.addLload(var);
1692                if (doDup && isPost)
1693                    bytecode.addOpcode(DUP2);
1694
1695                bytecode.addLconst((long)1);
1696                bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
1697                if (doDup && !isPost)
1698                    bytecode.addOpcode(DUP2);
1699
1700                bytecode.addLstore(var);
1701            }
1702            else if (t == FLOAT) {
1703                bytecode.addFload(var);
1704                if (doDup && isPost)
1705                    bytecode.addOpcode(DUP);
1706
1707                bytecode.addFconst(1.0f);
1708                bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
1709                if (doDup && !isPost)
1710                    bytecode.addOpcode(DUP);
1711
1712                bytecode.addFstore(var);
1713            }
1714            else if (t == BYTE || t == CHAR || t == SHORT || t == INT) {
1715                if (doDup && isPost)
1716                    bytecode.addIload(var);
1717
1718                bytecode.addOpcode(IINC);
1719                bytecode.add(var);
1720                bytecode.add(token == PLUSPLUS ? 1 : -1);
1721
1722                if (doDup && !isPost)
1723                    bytecode.addIload(var);
1724            }
1725            else
1726                badType(expr);
1727        }
1728        else {
1729            if (oprand instanceof Expr) {
1730                Expr e = (Expr)oprand;
1731                if (e.getOperator() == ARRAY) {
1732                    atArrayPlusPlus(token, isPost, e, doDup);
1733                    return;
1734                }
1735            }
1736
1737            atFieldPlusPlus(token, isPost, oprand, expr, doDup);
1738        }
1739    }
1740
1741    public void atArrayPlusPlus(int token, boolean isPost,
1742                        Expr expr, boolean doDup) throws CompileError
1743    {
1744        arrayAccess(expr.oprand1(), expr.oprand2());
1745        int t = exprType;
1746        int dim = arrayDim;
1747        if (dim > 0)
1748            badType(expr);
1749
1750        bytecode.addOpcode(DUP2);
1751        bytecode.addOpcode(getArrayReadOp(t, arrayDim));
1752        int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
1753        atPlusPlusCore(dup_code, doDup, token, isPost, expr);
1754        bytecode.addOpcode(getArrayWriteOp(t, dim));
1755    }
1756
1757    protected void atPlusPlusCore(int dup_code, boolean doDup,
1758                                  int token, boolean isPost,
1759                                  Expr expr) throws CompileError
1760    {
1761        int t = exprType;
1762
1763        if (doDup && isPost)
1764            bytecode.addOpcode(dup_code);
1765
1766        if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
1767            bytecode.addIconst(1);
1768            bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
1769            exprType = INT;
1770        }
1771        else if (t == LONG) {
1772            bytecode.addLconst((long)1);
1773            bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
1774        }
1775        else if (t == FLOAT) {
1776            bytecode.addFconst(1.0f);
1777            bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
1778        }
1779        else if (t == DOUBLE) {
1780            bytecode.addDconst(1.0);
1781            bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
1782        }
1783        else
1784            badType(expr);
1785
1786        if (doDup && !isPost)
1787            bytecode.addOpcode(dup_code);
1788    }
1789
1790    protected abstract void atFieldPlusPlus(int token, boolean isPost,
1791                ASTree oprand, Expr expr, boolean doDup) throws CompileError;
1792
1793    public abstract void atMember(Member n) throws CompileError;
1794
1795    public void atVariable(Variable v) throws CompileError {
1796        Declarator d = v.getDeclarator();
1797        exprType = d.getType();
1798        arrayDim = d.getArrayDim();
1799        className = d.getClassName();
1800        int var = getLocalVar(d);
1801
1802        if (arrayDim > 0)
1803            bytecode.addAload(var);
1804        else
1805            switch (exprType) {
1806            case CLASS :
1807                bytecode.addAload(var);
1808                break;
1809            case LONG :
1810                bytecode.addLload(var);
1811                break;
1812            case FLOAT :
1813                bytecode.addFload(var);
1814                break;
1815            case DOUBLE :
1816                bytecode.addDload(var);
1817                break;
1818            default : // BOOLEAN, BYTE, CHAR, SHORT, INT
1819
bytecode.addIload(var);
1820                break;
1821            }
1822    }
1823
1824    public void atKeyword(Keyword k) throws CompileError {
1825        arrayDim = 0;
1826        int token = k.get();
1827        switch (token) {
1828        case TRUE :
1829            bytecode.addIconst(1);
1830            exprType = BOOLEAN;
1831            break;
1832        case FALSE :
1833            bytecode.addIconst(0);
1834            exprType = BOOLEAN;
1835            break;
1836        case NULL :
1837            bytecode.addOpcode(ACONST_NULL);
1838            exprType = NULL;
1839            break;
1840        case THIS :
1841        case SUPER :
1842            if (inStaticMethod)
1843                throw new CompileError("not-available: "
1844                                       + (token == THIS ? "this" : "super"));
1845
1846            bytecode.addAload(0);
1847            exprType = CLASS;
1848            if (token == THIS)
1849                className = getThisName();
1850            else
1851                className = getSuperName();
1852            break;
1853        default :
1854            fatal();
1855        }
1856    }
1857
1858    public void atStringL(StringL s) throws CompileError {
1859        exprType = CLASS;
1860        arrayDim = 0;
1861        className = jvmJavaLangString;
1862        bytecode.addLdc(s.get());
1863    }
1864
1865    public void atIntConst(IntConst i) throws CompileError {
1866        arrayDim = 0;
1867        long value = i.get();
1868        int type = i.getType();
1869        if (type == IntConstant || type == CharConstant) {
1870            exprType = (type == IntConstant ? INT : CHAR);
1871            bytecode.addIconst((int)value);
1872        }
1873        else {
1874            exprType = LONG;
1875            bytecode.addLconst(value);
1876        }
1877    }
1878
1879    public void atDoubleConst(DoubleConst d) throws CompileError {
1880        arrayDim = 0;
1881        if (d.getType() == DoubleConstant) {
1882            exprType = DOUBLE;
1883            bytecode.addDconst(d.get());
1884        }
1885        else {
1886            exprType = FLOAT;
1887            bytecode.addFconst((float)d.get());
1888        }
1889    }
1890}
1891
Popular Tags