/Users/lyon/j4p/src/javassist/compiler/CodeGen.java

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