/Users/lyon/j4p/src/javassist/CtBehavior.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; 
17    
18   import javassist.bytecode.*; 
19   import javassist.compiler.Javac; 
20   import javassist.compiler.CompileError; 
21   import javassist.expr.ExprEditor; 
22    
23   /** 
24    * <code>CtBehavior</code> is the abstract super class of 
25    * <code>CtMethod</code> and <code>CtConstructor</code>. 
26    */ 
27   public abstract class CtBehavior extends CtMember { 
28       protected MethodInfo methodInfo; 
29    
30       protected CtBehavior(CtClass clazz, MethodInfo minfo) { 
31           super(clazz); 
32           methodInfo = minfo; 
33       } 
34    
35       /** 
36        * Returns the MethodInfo representing this member in the 
37        * class file. 
38        */ 
39       public MethodInfo getMethodInfo() { 
40           declaringClass.checkModify(); 
41           return methodInfo; 
42       } 
43    
44       /** 
45        * Undocumented method.  Do not use; internal-use only. 
46        */ 
47       public MethodInfo getMethodInfo2() { 
48           return methodInfo; 
49       } 
50    
51       /** 
52        * Obtains the modifiers of the member. 
53        * 
54        * @return          modifiers encoded with 
55        *                  <code>javassist.Modifier</code>. 
56        * @see Modifier 
57        */ 
58       public int getModifiers() { 
59           return AccessFlag.toModifier(methodInfo.getAccessFlags()); 
60       } 
61    
62       /** 
63        * Sets the encoded modifiers of the member. 
64        * 
65        * @see Modifier 
66        */ 
67       public void setModifiers(int mod) { 
68           declaringClass.checkModify(); 
69           methodInfo.setAccessFlags(AccessFlag.of(mod)); 
70       } 
71    
72       /** 
73        * Obtains the name of this member. 
74        * 
75        * @see CtConstructor#getName() 
76        */ 
77       public abstract String getName(); 
78    
79       /** 
80        * Obtains parameter types of this member. 
81        */ 
82       public CtClass[] getParameterTypes() throws NotFoundException { 
83           return Descriptor.getParameterTypes(methodInfo.getDescriptor(), 
84                   declaringClass.getClassPool()); 
85       } 
86    
87       /** 
88        * Obtains the type of the returned value. 
89        */ 
90       CtClass getReturnType0() throws NotFoundException { 
91           return Descriptor.getReturnType(methodInfo.getDescriptor(), 
92                   declaringClass.getClassPool()); 
93       } 
94    
95       /** 
96        * Returns the character string representing the parameter types 
97        * and the return type.  If two members have the same parameter types 
98        * and the return type, <code>getSignature()</code> returns the 
99        * same string. 
100       */ 
101      public String getSignature() { 
102          return methodInfo.getDescriptor(); 
103      } 
104   
105      /** 
106       * Obtains exceptions that this member may throw. 
107       */ 
108      public CtClass[] getExceptionTypes() throws NotFoundException { 
109          String[] exceptions; 
110          ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); 
111          if (ea == null) 
112              exceptions = null; 
113          else 
114              exceptions = ea.getExceptions(); 
115   
116          return declaringClass.getClassPool().get(exceptions); 
117      } 
118   
119      /** 
120       * Sets exceptions that this member may throw. 
121       */ 
122      public void setExceptionTypes(CtClass[] types) throws NotFoundException { 
123          declaringClass.checkModify(); 
124          if (types == null) { 
125              methodInfo.removeExceptionsAttribute(); 
126              return; 
127          } 
128   
129          String[] names = new String[types.length]; 
130          for (int i = 0; i < types.length; ++i) 
131              names[i] = types[i].getName(); 
132   
133          ExceptionsAttribute ea = methodInfo.getExceptionsAttribute(); 
134          if (ea == null) { 
135              ea = new ExceptionsAttribute(methodInfo.getConstPool()); 
136              methodInfo.setExceptionsAttribute(ea); 
137          } 
138   
139          ea.setExceptions(names); 
140      } 
141   
142      /** 
143       * Returns true if the body is empty. 
144       */ 
145      public abstract boolean isEmpty(); 
146   
147      /** 
148       * Sets a member body. 
149       * 
150       * @param src       the source code representing the member body. 
151       *                  It must be a single statement or block. 
152       *                  If it is <code>null</code>, the substituted member 
153       *                  body does nothing except returning zero or null. 
154       */ 
155      public void setBody(String src) throws CannotCompileException { 
156          declaringClass.checkModify(); 
157          try { 
158              Javac jv = new Javac(declaringClass); 
159              Bytecode b = jv.compileBody(this, src); 
160              methodInfo.setCodeAttribute(b.toCodeAttribute()); 
161              methodInfo.setAccessFlags(methodInfo.getAccessFlags() 
162                      & ~AccessFlag.ABSTRACT); 
163          } catch (CompileError e) { 
164              throw new CannotCompileException(e); 
165          } 
166      } 
167   
168      static void setBody0(CtClass srcClass, MethodInfo srcInfo, 
169                           CtClass destClass, MethodInfo destInfo, 
170                           ClassMap map) 
171              throws CannotCompileException { 
172          destClass.checkModify(); 
173   
174          if (map == null) 
175              map = new ClassMap(); 
176   
177          map.put(srcClass.getName(), destClass.getName()); 
178          try { 
179              CodeAttribute cattr = srcInfo.getCodeAttribute(); 
180              if (cattr != null) { 
181                  ConstPool cp = destInfo.getConstPool(); 
182                  CodeAttribute ca = (CodeAttribute) cattr.copy(cp, map); 
183                  destInfo.setCodeAttribute(ca); 
184              } 
185          } catch (CodeAttribute.RuntimeCopyException e) { 
186              /* the exception may be thrown by copy() in CodeAttribute. 
187               */ 
188              throw new CannotCompileException(e); 
189          } 
190   
191          destInfo.setAccessFlags(destInfo.getAccessFlags() 
192                  & ~AccessFlag.ABSTRACT); 
193      } 
194   
195      /** 
196       * Obtains an attribute with the given name. 
197       * If that attribute is not found in the class file, this 
198       * method returns null. 
199       * 
200       * @param name              attribute name 
201       */ 
202      public byte[] getAttribute(String name) { 
203          AttributeInfo ai = methodInfo.getAttribute(name); 
204          if (ai == null) 
205              return null; 
206          else 
207              return ai.get(); 
208      } 
209   
210      /** 
211       * Adds an attribute. The attribute is saved in the class file. 
212       * 
213       * @param name      attribute name 
214       * @param data      attribute value 
215       */ 
216      public void setAttribute(String name, byte[] data) { 
217          declaringClass.checkModify(); 
218          methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(), 
219                  name, data)); 
220      } 
221   
222      /** 
223       * Declares to use <code>$cflow</code> for this member; 
224       * If <code>$cflow</code> is used, the class files modified 
225       * with Javassist requires a support class 
226       * <code>javassist.runtime.Cflow</code> at runtime 
227       * (other Javassist classes are not required at runtime). 
228       * 
229       * <p>Every <code>$cflow</code> variable is given a unique name. 
230       * For example, if the given name is <code>"Point.paint"</code>, 
231       * then the variable is indicated by <code>$cflow(Point.paint)</code>. 
232       * 
233       * @param name      <code>$cflow</code> name.  It can include 
234       *                  alphabets, numbers, <code>_</code>, 
235       *                  <code>$</code>, and <code>.</code> (dot). 
236       * 
237       * @see javassist.runtime.Cflow 
238       */ 
239      public void useCflow(String name) throws CannotCompileException { 
240          CtClass cc = declaringClass; 
241          cc.checkModify(); 
242          ClassPool pool = cc.getClassPool(); 
243          String fname; 
244          int i = 0; 
245          while (true) { 
246              fname = "_cflow$" + i++; 
247              try { 
248                  cc.getDeclaredField(fname); 
249              } catch (NotFoundException e) { 
250                  break; 
251              } 
252          } 
253   
254          pool.recordCflow(name, declaringClass.getName(), fname); 
255          try { 
256              CtClass type = pool.get("javassist.runtime.Cflow"); 
257              CtField field = new CtField(type, fname, cc); 
258              field.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 
259              cc.addField(field, CtField.Initializer.byNew(type)); 
260              insertBefore(fname + ".enter();"); 
261              String src = fname + ".exit();"; 
262              insertAfter(src, true); 
263          } catch (NotFoundException e) { 
264              throw new CannotCompileException(e); 
265          } 
266      } 
267   
268      /** 
269       * Modifies the member body. 
270       * 
271       * @param converter         specifies how to modify. 
272       */ 
273      public void instrument(CodeConverter converter) 
274              throws CannotCompileException { 
275          declaringClass.checkModify(); 
276          ConstPool cp = methodInfo.getConstPool(); 
277          converter.doit(getDeclaringClass(), methodInfo, cp); 
278      } 
279   
280      /** 
281       * Modifies the member body. 
282       * 
283       * @param editor            specifies how to modify. 
284       */ 
285      public void instrument(ExprEditor editor) 
286              throws CannotCompileException { 
287          // if the class is not frozen, 
288          // does not trun the modified flag on. 
289          if (declaringClass.isFrozen()) 
290              declaringClass.checkModify(); 
291   
292          if (editor.doit(declaringClass, methodInfo)) 
293              declaringClass.checkModify(); 
294      } 
295   
296      /** 
297       * Inserts bytecode at the beginning of the body. 
298       * 
299       * @param src       the source code representing the inserted bytecode. 
300       *                  It must be a single statement or block. 
301       */ 
302      public void insertBefore(String src) throws CannotCompileException { 
303          declaringClass.checkModify(); 
304          CodeAttribute ca = methodInfo.getCodeAttribute(); 
305          if (ca == null) 
306              throw new CannotCompileException("no method body"); 
307   
308          CodeIterator iterator = ca.iterator(); 
309          Javac jv = new Javac(declaringClass); 
310          try { 
311              jv.recordParams(getParameterTypes(), 
312                      Modifier.isStatic(getModifiers())); 
313              jv.compileStmnt(src); 
314              Bytecode b = jv.getBytecode(); 
315              int stack = b.getMaxStack(); 
316              int locals = b.getMaxLocals(); 
317   
318              if (stack > ca.getMaxStack()) 
319                  ca.setMaxStack(stack); 
320   
321              if (locals > ca.getMaxLocals()) 
322                  ca.setMaxLocals(locals); 
323   
324              int pos = iterator.insertEx(b.get()); 
325              iterator.insert(b.getExceptionTable(), pos); 
326          } catch (NotFoundException e) { 
327              throw new CannotCompileException(e); 
328          } catch (CompileError e) { 
329              throw new CannotCompileException(e); 
330          } catch (BadBytecode e) { 
331              throw new CannotCompileException(e); 
332          } 
333      } 
334   
335      /** 
336       * Inserts bytecode at the end of the body. 
337       * The bytecode is inserted just before every return insturction. 
338       * It is not executed when an exception is thrown. 
339       * 
340       * @param src       the source code representing the inserted bytecode. 
341       *                  It must be a single statement or block. 
342       */ 
343      public void insertAfter(String src) 
344              throws CannotCompileException { 
345          insertAfter(src, false); 
346      } 
347   
348      /** 
349       * Inserts bytecode at the end of the body. 
350       * The bytecode is inserted just before every return insturction. 
351       * 
352       * @param src       the source code representing the inserted bytecode. 
353       *                  It must be a single statement or block. 
354       * @param asFinally         true if the inserted bytecode is executed 
355       *                  not only when the control normally returns 
356       *                  but also when an exception is thrown. 
357       */ 
358      public void insertAfter(String src, boolean asFinally) 
359              throws CannotCompileException { 
360          declaringClass.checkModify(); 
361          ConstPool pool = methodInfo.getConstPool(); 
362          CodeAttribute ca = methodInfo.getCodeAttribute(); 
363          if (ca == null) 
364              throw new CannotCompileException("no method body"); 
365   
366          CodeIterator iterator = ca.iterator(); 
367          int retAddr = ca.getMaxLocals(); 
368          Bytecode b = new Bytecode(pool, 0, retAddr + 1); 
369          b.setStackDepth(ca.getMaxStack() + 1); 
370          Javac jv = new Javac(b, declaringClass); 
371          try { 
372              jv.recordParams(getParameterTypes(), 
373                      Modifier.isStatic(getModifiers())); 
374              CtClass rtype = getReturnType0(); 
375              int varNo = jv.recordReturnType(rtype, true); 
376   
377              int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo); 
378   
379              byte[] save = makeSaveCode(pool, rtype, varNo); 
380              byte[] restore = makeRestoreCode(b, pool, rtype, varNo); 
381   
382              b.addAstore(retAddr); 
383              jv.compileStmnt(src); 
384              b.addRet(retAddr); 
385              ca.setMaxStack(b.getMaxStack()); 
386              ca.setMaxLocals(b.getMaxLocals()); 
387   
388              int gapPos = iterator.append(b.get()); 
389              iterator.append(b.getExceptionTable(), gapPos); 
390   
391              if (asFinally) 
392                  ca.getExceptionTable().add(0, gapPos, gapPos, 0); 
393   
394              int gapLen = iterator.getCodeLength() - gapPos - handlerLen; 
395              int subr = iterator.getCodeLength() - gapLen; 
396   
397              while (iterator.hasNext()) { 
398                  int pos = iterator.next(); 
399                  if (pos >= subr) 
400                      break; 
401   
402                  int c = iterator.byteAt(pos); 
403                  if (c == Opcode.ARETURN || c == Opcode.IRETURN 
404                          || c == Opcode.FRETURN || c == Opcode.LRETURN 
405                          || c == Opcode.DRETURN || c == Opcode.RETURN) { 
406                      insertJSR(iterator, subr, pos, save, restore); 
407                      subr = iterator.getCodeLength() - gapLen; 
408                  } 
409              } 
410          } catch (NotFoundException e) { 
411              throw new CannotCompileException(e); 
412          } catch (CompileError e) { 
413              throw new CannotCompileException(e); 
414          } catch (BadBytecode e) { 
415              throw new CannotCompileException(e); 
416          } 
417      } 
418   
419      private byte[] makeSaveCode(ConstPool cp, CtClass rtype, int varNo) { 
420          Bytecode b = new Bytecode(cp, 0, 0); 
421          if (rtype == CtClass.voidType) { 
422              b.addOpcode(Opcode.ACONST_NULL); 
423              b.addAstore(varNo); 
424              return b.get(); 
425          } else { 
426              b.addStore(varNo, rtype); 
427              return b.get(); 
428          } 
429      } 
430   
431      private byte[] makeRestoreCode(Bytecode code, ConstPool cp, 
432                                     CtClass rtype, int varNo) { 
433          if (rtype == CtClass.voidType) { 
434              if (code.getMaxLocals() < 1) 
435                  code.setMaxLocals(1); 
436   
437              return new byte[0]; 
438          } else { 
439              Bytecode b = new Bytecode(cp, 0, 0); 
440              b.addLoad(varNo, rtype); 
441              return b.get(); 
442          } 
443      } 
444   
445      private void insertJSR(CodeIterator iterator, int subr, int pos, 
446                             byte[] save, byte[] restore) 
447              throws BadBytecode { 
448          int gapSize = 5 + save.length + restore.length; 
449          boolean wide = subr - pos > Short.MAX_VALUE - gapSize - 4; 
450          gapSize = iterator.insertGap(pos, wide ? gapSize : gapSize - 2); 
451   
452          iterator.write(save, pos); 
453          pos += save.length; 
454          if (wide) { 
455              iterator.writeByte(Opcode.JSR_W, pos); 
456              iterator.write32bit(subr - pos + gapSize, pos + 1); 
457              pos += 5; 
458          } else { 
459              iterator.writeByte(Opcode.JSR, pos); 
460              iterator.write16bit(subr - pos + gapSize, pos + 1); 
461              pos += 3; 
462          } 
463   
464          iterator.write(restore, pos); 
465      } 
466   
467      private int insertAfterHandler(boolean asFinally, Bytecode b, 
468                                     CtClass rtype, int returnVarNo) { 
469          if (!asFinally) 
470              return 0; 
471   
472          int var = b.getMaxLocals(); 
473          b.incMaxLocals(1); 
474          int pc = b.currentPc(); 
475          b.addAstore(var); 
476          if (rtype.isPrimitive()) { 
477              char c = ((CtPrimitiveType) rtype).getDescriptor(); 
478              if (c == 'D') { 
479                  b.addDconst(0.0); 
480                  b.addDstore(returnVarNo); 
481              } else if (c == 'F') { 
482                  b.addFconst(0); 
483                  b.addFstore(returnVarNo); 
484              } else if (c == 'J') { 
485                  b.addLconst(0); 
486                  b.addLstore(returnVarNo); 
487              } else if (c != 'V') { // int, boolean, char, short, ... 
488                  b.addIconst(0); 
489                  b.addIstore(returnVarNo); 
490              } 
491          } else { 
492              b.addOpcode(Opcode.ACONST_NULL); 
493              b.addAstore(returnVarNo); 
494          } 
495   
496          b.addOpcode(Opcode.JSR); 
497          int pc2 = b.currentPc(); 
498          b.addIndex(0);  // correct later 
499          b.addAload(var); 
500          b.addOpcode(Opcode.ATHROW); 
501          int pc3 = b.currentPc(); 
502          b.write16bit(pc2, pc3 - pc2 + 1); 
503          return pc3 - pc; 
504      } 
505   
506      /* -- OLD version -- 
507   
508      public void insertAfter(String src) throws CannotCompileException { 
509          declaringClass.checkModify(); 
510          CodeAttribute ca = methodInfo.getCodeAttribute(); 
511          CodeIterator iterator = ca.iterator(); 
512          Bytecode b = new Bytecode(methodInfo.getConstPool(), 
513                                    ca.getMaxStack(), ca.getMaxLocals()); 
514          b.setStackDepth(ca.getMaxStack()); 
515          Javac jv = new Javac(b, declaringClass); 
516          try { 
517              jv.recordParams(getParameterTypes(), 
518                              Modifier.isStatic(getModifiers())); 
519              CtClass rtype = getReturnType0(); 
520              int varNo = jv.recordReturnType(rtype, true); 
521              boolean isVoid = rtype == CtClass.voidType; 
522              if (isVoid) { 
523                  b.addOpcode(Opcode.ACONST_NULL); 
524                  b.addAstore(varNo); 
525                  jv.compileStmnt(src); 
526              } 
527              else { 
528                  b.addStore(varNo, rtype); 
529                  jv.compileStmnt(src); 
530                  b.addLoad(varNo, rtype); 
531              } 
532   
533              byte[] code = b.get(); 
534              ca.setMaxStack(b.getMaxStack()); 
535              ca.setMaxLocals(b.getMaxLocals()); 
536              while (iterator.hasNext()) { 
537                  int pos = iterator.next(); 
538                  int c = iterator.byteAt(pos); 
539                  if (c == Opcode.ARETURN || c == Opcode.IRETURN 
540                      || c == Opcode.FRETURN || c == Opcode.LRETURN 
541                      || c == Opcode.DRETURN || c == Opcode.RETURN) 
542                      iterator.insert(pos, code); 
543              } 
544          } 
545          catch (NotFoundException e) { 
546              throw new CannotCompileException(e); 
547          } 
548          catch (CompileError e) { 
549              throw new CannotCompileException(e); 
550          } 
551          catch (BadBytecode e) { 
552              throw new CannotCompileException(e); 
553          } 
554      } 
555      */ 
556   
557      /** 
558       * Adds a catch clause that handles an exception thrown in the 
559       * body.  The catch clause must end with a return or throw statement. 
560       * 
561       * @param src       the source code representing the catch clause. 
562       *                  It must be a single statement or block. 
563       * @param exceptionType     the type of the exception handled by the 
564       *                          catch clause. 
565       */ 
566      public void addCatch(String src, CtClass exceptionType) 
567              throws CannotCompileException { 
568          addCatch(src, exceptionType, "$e"); 
569      } 
570   
571      /** 
572       * Adds a catch clause that handles an exception thrown in the 
573       * body.  The catch clause must end with a return or throw statement. 
574       * 
575       * @param src       the source code representing the catch clause. 
576       *                  It must be a single statement or block. 
577       * @param exceptionType     the type of the exception handled by the 
578       *                          catch clause. 
579       * @param exceptionName     the name of the variable containing the 
580       *                          caught exception, for example, 
581       *                          <code>$e</code>. 
582       */ 
583      public void addCatch(String src, CtClass exceptionType, 
584                           String exceptionName) 
585              throws CannotCompileException { 
586          declaringClass.checkModify(); 
587          ConstPool cp = methodInfo.getConstPool(); 
588          CodeAttribute ca = methodInfo.getCodeAttribute(); 
589          CodeIterator iterator = ca.iterator(); 
590          Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals()); 
591          b.setStackDepth(1); 
592          Javac jv = new Javac(b, declaringClass); 
593          try { 
594              jv.recordParams(getParameterTypes(), 
595                      Modifier.isStatic(getModifiers())); 
596              int var = jv.recordVariable(exceptionType, exceptionName); 
597              b.addAstore(var); 
598              jv.compileStmnt(src); 
599   
600              int stack = b.getMaxStack(); 
601              int locals = b.getMaxLocals(); 
602   
603              if (stack > ca.getMaxStack()) 
604                  ca.setMaxStack(stack); 
605   
606              if (locals > ca.getMaxLocals()) 
607                  ca.setMaxLocals(locals); 
608   
609              int len = iterator.getCodeLength(); 
610              int pos = iterator.append(b.get()); 
611              ca.getExceptionTable().add(0, len, len, 
612                      cp.addClassInfo(exceptionType)); 
613              iterator.append(b.getExceptionTable(), pos); 
614          } catch (NotFoundException e) { 
615              throw new CannotCompileException(e); 
616          } catch (CompileError e) { 
617              throw new CannotCompileException(e); 
618          } 
619      } 
620  } 
621