/Users/lyon/j4p/src/javassist/compiler/JvstCodeGen.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 javassist.*; 
19   import javassist.bytecode.*; 
20   import javassist.compiler.ast.*; 
21    
22   /* Code generator methods for extensions by Javassist. 
23    */ 
24    
25   public class JvstCodeGen extends MemberCodeGen { 
26       private String paramArrayName = null; 
27       private String paramListName = null; 
28       private CtClass[] paramTypeList = null; 
29       private int paramVarBase = 0;       // variable index for $0 or $1. 
30       private boolean useParam0 = false;  // true if $0 is used. 
31       private String param0Type = null;   // JVM name 
32       private static final String sigName = "$sig"; 
33       private static final String dollarTypeName = "$type"; 
34       private static final String clazzName = "$class"; 
35       private CtClass dollarType = null; 
36       private CtClass returnType = null; 
37       private String returnCastName = null; 
38       private String returnVarName = null;        // null if $_ is not used. 
39       private static final String wrapperCastName = "$w"; 
40       private String proceedName = null; 
41       private static final String cflowName = "$cflow"; 
42       private ProceedHandler procHandler = null;  // null if not used. 
43    
44       public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) { 
45           super(b, cc, cp); 
46       } 
47    
48       /* Index of $1. 
49        */ 
50       private int indexOfParam1() { 
51           return paramVarBase + (useParam0 ? 1 : 0); 
52       } 
53    
54       /* Records a ProceedHandler obejct. 
55        * 
56        * @param name      the name of the special method call. 
57        *                  it is usually $proceed. 
58        */ 
59       public void setProceedHandler(ProceedHandler h, String name) { 
60           proceedName = name; 
61           procHandler = h; 
62       } 
63    
64       /* If the type of the expression compiled last is void, 
65        * add ACONST_NULL and change exprType, arrayDim, className. 
66        */ 
67       public void addNullIfVoid() { 
68           if (exprType == VOID) { 
69               bytecode.addOpcode(ACONST_NULL); 
70               exprType = CLASS; 
71               arrayDim = 0; 
72               className = jvmJavaLangObject; 
73           } 
74       } 
75    
76       /* To support $args, $sig, and $type. 
77        * $args is an array of parameter list. 
78        */ 
79       public void atMember(Member mem) throws CompileError { 
80           String name = mem.get(); 
81           if (name.equals(paramArrayName)) { 
82               compileParameterList(bytecode, paramTypeList, indexOfParam1()); 
83               exprType = CLASS; 
84               arrayDim = 1; 
85               className = jvmJavaLangObject; 
86           } else if (name.equals(sigName)) { 
87               bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList)); 
88               bytecode.addInvokestatic("javassist/runtime/Desc", "getParams", 
89                       "(Ljava/lang/String;)[Ljava/lang/Class;"); 
90               exprType = CLASS; 
91               arrayDim = 1; 
92               className = "java/lang/Class"; 
93           } else if (name.equals(dollarTypeName)) { 
94               if (dollarType == null) 
95                   throw new CompileError(dollarType + " is not available"); 
96    
97               bytecode.addLdc(Descriptor.of(dollarType)); 
98               callGetType("getType"); 
99           } else if (name.equals(clazzName)) { 
100              if (param0Type == null) 
101                  throw new CompileError(clazzName + " is not available"); 
102   
103              bytecode.addLdc(param0Type); 
104              callGetType("getClazz"); 
105          } else 
106              super.atMember(mem); 
107      } 
108   
109      private void callGetType(String method) { 
110          bytecode.addInvokestatic("javassist/runtime/Desc", method, 
111                  "(Ljava/lang/String;)Ljava/lang/Class;"); 
112          exprType = CLASS; 
113          arrayDim = 0; 
114          className = "java/lang/Class"; 
115      } 
116   
117      private void atSigOrType(String sig) throws CompileError { 
118      } 
119   
120      protected void atFieldAssign(Expr expr, int op, ASTree left, 
121                                   ASTree right, boolean doDup) throws CompileError { 
122          if (left instanceof Member 
123                  && ((Member) left).get().equals(paramArrayName)) { 
124              if (op != '=') 
125                  throw new CompileError("bad operator for " + paramArrayName); 
126   
127              right.accept(this); 
128              if (arrayDim != 1 || exprType != CLASS) 
129                  throw new CompileError("invalid type for " + paramArrayName); 
130   
131              atAssignParamList(paramTypeList, bytecode); 
132              if (!doDup) 
133                  bytecode.addOpcode(POP); 
134          } else 
135              super.atFieldAssign(expr, op, left, right, doDup); 
136      } 
137   
138      protected void atAssignParamList(CtClass[] params, Bytecode code) 
139              throws CompileError { 
140          if (params == null) 
141              return; 
142   
143          int varNo = indexOfParam1(); 
144          int n = params.length; 
145          for (int i = 0; i < n; ++i) { 
146              code.addOpcode(DUP); 
147              code.addIconst(i); 
148              code.addOpcode(AALOAD); 
149              compileUnwrapValue(params[i], code); 
150              code.addStore(varNo, params[i]); 
151              varNo += is2word(exprType, arrayDim) ? 2 : 1; 
152          } 
153      } 
154   
155      public void atCastExpr(CastExpr expr) throws CompileError { 
156          ASTList classname = expr.getClassName(); 
157          if (classname != null && expr.getArrayDim() == 0) { 
158              ASTree p = classname.head(); 
159              if (p instanceof Symbol && classname.tail() == null) { 
160                  String typename = ((Symbol) p).get(); 
161                  if (typename.equals(returnCastName)) { 
162                      atCastToRtype(expr); 
163                      return; 
164                  } else if (typename.equals(wrapperCastName)) { 
165                      atCastToWrapper(expr); 
166                      return; 
167                  } 
168              } 
169          } 
170   
171          super.atCastExpr(expr); 
172      } 
173   
174      /** 
175       * Inserts a cast operator to the return type. 
176       * If the return type is void, this does nothing. 
177       */ 
178      protected void atCastToRtype(CastExpr expr) throws CompileError { 
179          expr.getOprand().accept(this); 
180          if (exprType == VOID || isRefType(exprType) || arrayDim > 0) 
181              compileUnwrapValue(returnType, bytecode); 
182          else if (returnType instanceof CtPrimitiveType) { 
183              CtPrimitiveType pt = (CtPrimitiveType) returnType; 
184              int destType = jvmTypeNameToExprType(pt.getDescriptor()); 
185              atNumCastExpr(exprType, destType); 
186              exprType = destType; 
187              arrayDim = 0; 
188              className = null; 
189          } else 
190              throw new CompileError("invalid cast"); 
191      } 
192   
193      protected void atCastToWrapper(CastExpr expr) throws CompileError { 
194          expr.getOprand().accept(this); 
195          if (isRefType(exprType) || arrayDim > 0) 
196              return;     // Object type.  do nothing. 
197   
198          CtClass clazz = lookupClass(exprType, arrayDim, className); 
199          if (clazz instanceof CtPrimitiveType) { 
200              CtPrimitiveType pt = (CtPrimitiveType) clazz; 
201              String wrapper = pt.getWrapperName(); 
202              bytecode.addNew(wrapper);           // new <wrapper> 
203              bytecode.addOpcode(DUP);            // dup 
204              if (pt.getDataSize() > 1) 
205                  bytecode.addOpcode(DUP2_X2);    // dup2_x2 
206              else 
207                  bytecode.addOpcode(DUP2_X1);    // dup2_x1 
208   
209              bytecode.addOpcode(POP2);           // pop2 
210              bytecode.addInvokespecial(wrapper, "<init>", 
211                      "(" + pt.getDescriptor() + ")V"); 
212              // invokespecial 
213              exprType = CLASS; 
214              arrayDim = 0; 
215              className = jvmJavaLangObject; 
216          } 
217      } 
218   
219      /* Delegates to a ProcHandler object if the method call is 
220       * $proceed().  It may process $cflow(). 
221       */ 
222      protected void atMethodCall(Expr expr) throws CompileError { 
223          ASTree method = expr.oprand1(); 
224          if (method instanceof Member) { 
225              String name = ((Member) method).get(); 
226              if (procHandler != null && name.equals(proceedName)) { 
227                  procHandler.doit(this, bytecode, (ASTList) expr.oprand2()); 
228                  return; 
229              } else if (name.equals(cflowName)) { 
230                  atCflow((ASTList) expr.oprand2()); 
231                  return; 
232              } 
233          } 
234   
235          super.atMethodCall(expr); 
236      } 
237   
238      /* To support $cflow(). 
239       */ 
240      protected void atCflow(ASTList cname) throws CompileError { 
241          StringBuffer sbuf = new StringBuffer(); 
242          if (cname == null || cname.tail() != null) 
243              throw new CompileError("bad " + cflowName); 
244   
245          makeCflowName(sbuf, cname.head()); 
246          String name = sbuf.toString(); 
247          Object[] names = classPool.lookupCflow(name); 
248          if (names == null) 
249              throw new CompileError("no such a " + cflowName + ": " + name); 
250   
251          bytecode.addGetstatic((String) names[0], (String) names[1], 
252                  "Ljavassist/runtime/Cflow;"); 
253          bytecode.addInvokevirtual("javassist.runtime.Cflow", 
254                  "value", "()I"); 
255          exprType = INT; 
256          arrayDim = 0; 
257          className = null; 
258      } 
259   
260      /* Syntax: 
261       * 
262       * <cflow> : $cflow '(' <cflow name> ')' 
263       * <cflow name> : <identifier> ('.' <identifier>)* 
264       */ 
265      private static void makeCflowName(StringBuffer sbuf, ASTree name) 
266              throws CompileError { 
267          if (name instanceof Symbol) { 
268              sbuf.append(((Symbol) name).get()); 
269              return; 
270          } else if (name instanceof Expr) { 
271              Expr expr = (Expr) name; 
272              if (expr.getOperator() == '.') { 
273                  makeCflowName(sbuf, expr.oprand1()); 
274                  sbuf.append('.'); 
275                  makeCflowName(sbuf, expr.oprand2()); 
276                  return; 
277              } 
278          } 
279   
280          throw new CompileError("bad " + cflowName); 
281      } 
282   
283      /* To support $$.  ($$) is equivalent to ($1, ..., $n). 
284       * It can be used only as a parameter list of method call. 
285       */ 
286      public boolean isParamListName(ASTList args) { 
287          if (paramTypeList != null 
288                  && args != null && args.tail() == null) { 
289              ASTree left = args.head(); 
290              return (left instanceof Member 
291                      && ((Member) left).get().equals(paramListName)); 
292          } else 
293              return false; 
294      } 
295   
296      /* 
297      public int atMethodArgsLength(ASTList args) { 
298          if (!isParamListName(args)) 
299              return super.atMethodArgsLength(args); 
300   
301          return paramTypeList.length; 
302      } 
303      */ 
304   
305      public int atMethodArgsLength(ASTList args) { 
306          String pname = paramListName; 
307          int n = 0; 
308          while (args != null) { 
309              ASTree a = args.head(); 
310              if (a instanceof Member && ((Member) a).get().equals(pname)) { 
311                  if (paramTypeList != null) 
312                      n += paramTypeList.length; 
313              } else 
314                  ++n; 
315   
316              args = args.tail(); 
317          } 
318   
319          return n; 
320      } 
321   
322      public void atMethodArgs(ASTList args, int[] types, int[] dims, 
323                               String[] cnames) throws CompileError { 
324          CtClass[] params = paramTypeList; 
325          String pname = paramListName; 
326          int i = 0; 
327          while (args != null) { 
328              ASTree a = args.head(); 
329              if (a instanceof Member && ((Member) a).get().equals(pname)) { 
330                  if (params != null) { 
331                      int n = params.length; 
332                      int regno = indexOfParam1(); 
333                      for (int k = 0; k < n; ++k) { 
334                          CtClass p = params[k]; 
335                          regno += bytecode.addLoad(regno, p); 
336                          setType(p); 
337                          types[i] = exprType; 
338                          dims[i] = arrayDim; 
339                          cnames[i] = className; 
340                          ++i; 
341                      } 
342                  } 
343              } else { 
344                  a.accept(this); 
345                  types[i] = exprType; 
346                  dims[i] = arrayDim; 
347                  cnames[i] = className; 
348                  ++i; 
349              } 
350   
351              args = args.tail(); 
352          } 
353      } 
354   
355      /* 
356      public void atMethodArgs(ASTList args, int[] types, int[] dims, 
357                                  String[] cnames) throws CompileError { 
358          if (!isParamListName(args)) { 
359              super.atMethodArgs(args, types, dims, cnames); 
360              return; 
361          } 
362   
363          CtClass[] params = paramTypeList; 
364          if (params == null) 
365              return; 
366   
367          int n = params.length; 
368          int regno = indexOfParam1(); 
369          for (int i = 0; i < n; ++i) { 
370              CtClass p = params[i]; 
371              regno += bytecode.addLoad(regno, p); 
372              setType(p); 
373              types[i] = exprType; 
374              dims[i] = arrayDim; 
375              cnames[i] = className; 
376          } 
377      } 
378      */ 
379   
380      /* 
381       * Makes it valid to write "return <expr>;" for a void method. 
382       */ 
383      protected void atReturnStmnt(Stmnt st) throws CompileError { 
384          ASTree result = st.getLeft(); 
385          if (result != null && returnType == CtClass.voidType) { 
386              result.accept(this); 
387              if (is2word(exprType, arrayDim)) 
388                  bytecode.addOpcode(POP2); 
389              else if (exprType != VOID) 
390                  bytecode.addOpcode(POP); 
391   
392              result = null; 
393          } 
394   
395          atReturnStmnt2(result); 
396      } 
397   
398      /** 
399       * Makes a cast to the return type ($r) available. 
400       * It also enables $_. 
401       * 
402       * <p>If the return type is void, ($r) does nothing. 
403       * The type of $_ is java.lang.Object. 
404       * 
405       * @param resultName        null if $_ is not used. 
406       * @return          -1 or the variable index assigned to $_. 
407       */ 
408      public int recordReturnType(CtClass type, String castName, 
409                                  String resultName, SymbolTable tbl) throws CompileError { 
410          returnType = type; 
411          returnCastName = castName; 
412          returnVarName = resultName; 
413          if (resultName == null) 
414              return -1; 
415          else { 
416              int varNo = getMaxLocals(); 
417              int locals = varNo + recordVar(type, resultName, varNo, tbl); 
418              setMaxLocals(locals); 
419              return varNo; 
420          } 
421      } 
422   
423      /** 
424       * Makes $type available. 
425       */ 
426      public void recordType(CtClass t) { 
427          dollarType = t; 
428      } 
429   
430      /** 
431       * Makes method parameters $0, $1, ..., $args, and $$ available. 
432       * $0 is equivalent to THIS if the method is not static.  Otherwise, 
433       * if the method is static, then $0 is not available. 
434       */ 
435      public void recordParams(CtClass[] params, boolean isStatic, 
436                               String prefix, String paramVarName, 
437                               String paramsName, SymbolTable tbl) 
438              throws CompileError { 
439          recordParams(params, isStatic, prefix, paramVarName, 
440                  paramsName, !isStatic, 0, getThisName(), tbl); 
441      } 
442   
443      /** 
444       * Makes method parameters $0, $1, ..., $args, and $$ available. 
445       * $0 is available only if use0 is true.  It might not be equivalent 
446       * to THIS. 
447       * 
448       * @paaram use0     true if $0 is used. 
449       * @param paramBase the register number of $0 (use0 is true) 
450       *                          or $1 (otherwise). 
451       * @param target    the class of $0.  If use0 is false, target 
452       *                  can be null. 
453       * @param isStatic  true if the method in which the compiled bytecode 
454       *                  is embedded is static. 
455       */ 
456      public void recordParams(CtClass[] params, boolean isStatic, 
457                               String prefix, String paramVarName, 
458                               String paramsName, boolean use0, 
459                               int paramBase, String target, 
460                               SymbolTable tbl) 
461              throws CompileError { 
462          int varNo; 
463   
464          paramTypeList = params; 
465          paramArrayName = paramVarName; 
466          paramListName = paramsName; 
467          paramVarBase = paramBase; 
468          useParam0 = use0; 
469   
470          param0Type = jvmToJavaName(target); 
471   
472          inStaticMethod = isStatic; 
473          varNo = paramBase; 
474          if (use0) { 
475              String varName = prefix + "0"; 
476              Declarator decl 
477                      = new Declarator(CLASS, javaToJvmName(target), 0, varNo++, 
478                              new Symbol(varName)); 
479              tbl.append(varName, decl); 
480          } 
481   
482          for (int i = 0; i < params.length; ++i) 
483              varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl); 
484   
485          if (getMaxLocals() < varNo) 
486              setMaxLocals(varNo); 
487      } 
488   
489      /** 
490       * Makes the given variable name available. 
491       * 
492       * @param type      variable type 
493       * @param varName   variable name 
494       */ 
495      public int recordVariable(CtClass type, String varName, SymbolTable tbl) 
496              throws CompileError { 
497          if (varName == null) 
498              return -1; 
499          else { 
500              int varNo = getMaxLocals(); 
501              int locals = varNo + recordVar(type, varName, varNo, tbl); 
502              setMaxLocals(locals); 
503              return varNo; 
504          } 
505      } 
506   
507      private int recordVar(CtClass cc, String varName, int varNo, 
508                            SymbolTable tbl) throws CompileError { 
509          if (cc == CtClass.voidType) { 
510              exprType = CLASS; 
511              arrayDim = 0; 
512              className = jvmJavaLangObject; 
513          } else 
514              setType(cc); 
515   
516          Declarator decl 
517                  = new Declarator(exprType, className, arrayDim, 
518                          varNo, new Symbol(varName)); 
519          tbl.append(varName, decl); 
520          return is2word(exprType, arrayDim) ? 2 : 1; 
521      } 
522   
523      /* compileParameterList() returns the stack size used 
524       * by the produced code. 
525       * 
526       * This method correctly computes the max_stack value. 
527       * 
528       * @param regno     the index of the local variable in which 
529       *                  the first argument is received. 
530       *                  (0: static method, 1: regular method.) 
531       */ 
532      public static int compileParameterList(Bytecode code, 
533                                             CtClass[] params, int regno) { 
534          if (params == null) { 
535              code.addIconst(0);                          // iconst_0 
536              code.addAnewarray(javaLangObject);          // anewarray Object 
537              return 1; 
538          } else { 
539              CtClass[] args = new CtClass[1]; 
540              int n = params.length; 
541              code.addIconst(n);                          // iconst_<n> 
542              code.addAnewarray(javaLangObject);          // anewarray Object 
543              for (int i = 0; i < n; ++i) { 
544                  code.addOpcode(Bytecode.DUP);           // dup 
545                  code.addIconst(i);                      // iconst_<i> 
546                  if (params[i].isPrimitive()) { 
547                      CtPrimitiveType pt = (CtPrimitiveType) params[i]; 
548                      String wrapper = pt.getWrapperName(); 
549                      code.addNew(wrapper);               // new <wrapper> 
550                      code.addOpcode(Bytecode.DUP);       // dup 
551                      int s = code.addLoad(regno, pt);    // ?load <regno> 
552                      regno += s; 
553                      args[0] = pt; 
554                      code.addInvokespecial(wrapper, "<init>", 
555                              Descriptor.ofMethod(CtClass.voidType, args)); 
556                      // invokespecial 
557                  } else { 
558                      code.addAload(regno);               // aload <regno> 
559                      ++regno; 
560                  } 
561   
562                  code.addOpcode(Bytecode.AASTORE);       // aastore 
563              } 
564   
565              return 8; 
566          } 
567      } 
568   
569      protected void compileUnwrapValue(CtClass type, Bytecode code) 
570              throws CompileError { 
571          if (type == CtClass.voidType) { 
572              addNullIfVoid(); 
573              return; 
574          } 
575   
576          if (exprType == VOID) 
577              throw new CompileError("invalid type for " + returnCastName); 
578   
579          if (type instanceof CtPrimitiveType) { 
580              CtPrimitiveType pt = (CtPrimitiveType) type; 
581              // pt is not voidType. 
582              String wrapper = pt.getWrapperName(); 
583              code.addCheckcast(wrapper); 
584              code.addInvokevirtual(wrapper, pt.getGetMethodName(), 
585                      pt.getGetMethodDescriptor()); 
586              setType(type); 
587          } else { 
588              code.addCheckcast(type); 
589              setType(type); 
590          } 
591      } 
592   
593      /* Sets exprType, arrayDim, and className; 
594       * If type is void, then this method does nothing. 
595       */ 
596      public void setType(CtClass type) throws CompileError { 
597          setType(type, 0); 
598      } 
599   
600      private void setType(CtClass type, int dim) throws CompileError { 
601          if (type.isPrimitive()) { 
602              CtPrimitiveType pt = (CtPrimitiveType) type; 
603              exprType = descToType(pt.getDescriptor()); 
604              arrayDim = dim; 
605              className = null; 
606          } else if (type.isArray()) 
607              try { 
608                  setType(type.getComponentType(), dim + 1); 
609              } catch (NotFoundException e) { 
610                  throw new CompileError("undefined type: " + type.getName()); 
611              } 
612          else { 
613              exprType = CLASS; 
614              arrayDim = dim; 
615              className = javaToJvmName(type.getName()); 
616          } 
617      } 
618   
619      /* Performs implicit coercion from exprType to type. 
620       */ 
621      public void doNumCast(CtClass type) throws CompileError { 
622          if (arrayDim == 0 && !isRefType(exprType)) 
623              if (type instanceof CtPrimitiveType) { 
624                  CtPrimitiveType pt = (CtPrimitiveType) type; 
625                  atNumCastExpr(exprType, descToType(pt.getDescriptor())); 
626              } else 
627                  throw new CompileError("type mismatch"); 
628      } 
629  } 
630