• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist;
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> represents a method, a constructor,
25  * or a static constructor (class initializer).
26  * It is the abstract super class of
27  * <code>CtMethod</code> and <code>CtConstructor</code>.
28  */
29 public abstract class CtBehavior extends CtMember {
30     protected MethodInfo methodInfo;
31 
CtBehavior(CtClass clazz, MethodInfo minfo)32     protected CtBehavior(CtClass clazz, MethodInfo minfo) {
33         super(clazz);
34         methodInfo = minfo;
35     }
36 
37     /**
38      * @param isCons        true if this is a constructor.
39      */
copy(CtBehavior src, boolean isCons, ClassMap map)40     void copy(CtBehavior src, boolean isCons, ClassMap map)
41         throws CannotCompileException
42     {
43         CtClass declaring = declaringClass;
44         MethodInfo srcInfo = src.methodInfo;
45         CtClass srcClass = src.getDeclaringClass();
46         ConstPool cp = declaring.getClassFile2().getConstPool();
47 
48         map = new ClassMap(map);
49         map.put(srcClass.getName(), declaring.getName());
50         try {
51             boolean patch = false;
52             CtClass srcSuper = srcClass.getSuperclass();
53             CtClass destSuper = declaring.getSuperclass();
54             String destSuperName = null;
55             if (srcSuper != null && destSuper != null) {
56                 String srcSuperName = srcSuper.getName();
57                 destSuperName = destSuper.getName();
58                 if (!srcSuperName.equals(destSuperName))
59                     if (srcSuperName.equals(CtClass.javaLangObject))
60                         patch = true;
61                     else
62                         map.putIfNone(srcSuperName, destSuperName);
63             }
64 
65             // a stack map table is copied from srcInfo.
66             methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
67             if (isCons && patch)
68                 methodInfo.setSuperclass(destSuperName);
69         }
70         catch (NotFoundException e) {
71             throw new CannotCompileException(e);
72         }
73         catch (BadBytecode e) {
74             throw new CannotCompileException(e);
75         }
76     }
77 
extendToString(StringBuffer buffer)78     protected void extendToString(StringBuffer buffer) {
79         buffer.append(' ');
80         buffer.append(getName());
81         buffer.append(' ');
82         buffer.append(methodInfo.getDescriptor());
83     }
84 
85     /**
86      * Returns the method or constructor name followed by parameter types
87      * such as <code>javassist.CtBehavior.stBody(String)</code>.
88      *
89      * @since 3.5
90      */
getLongName()91     public abstract String getLongName();
92 
93     /**
94      * Returns the MethodInfo representing this method/constructor in the
95      * class file.
96      */
getMethodInfo()97     public MethodInfo getMethodInfo() {
98         declaringClass.checkModify();
99         return methodInfo;
100     }
101 
102     /**
103      * Returns the MethodInfo representing the method/constructor in the
104      * class file (read only).
105      * Normal applications do not need calling this method.  Use
106      * <code>getMethodInfo()</code>.
107      *
108      * <p>The <code>MethodInfo</code> object obtained by this method
109      * is read only.  Changes to this object might not be reflected
110      * on a class file generated by <code>toBytecode()</code>,
111      * <code>toClass()</code>, etc in <code>CtClass</code>.
112      *
113      * <p>This method is available even if the <code>CtClass</code>
114      * containing this method is frozen.  However, if the class is
115      * frozen, the <code>MethodInfo</code> might be also pruned.
116      *
117      * @see #getMethodInfo()
118      * @see CtClass#isFrozen()
119      * @see CtClass#prune()
120      */
getMethodInfo2()121     public MethodInfo getMethodInfo2() { return methodInfo; }
122 
123     /**
124      * Obtains the modifiers of the method/constructor.
125      *
126      * @return          modifiers encoded with
127      *                  <code>javassist.Modifier</code>.
128      * @see Modifier
129      */
getModifiers()130     public int getModifiers() {
131         return AccessFlag.toModifier(methodInfo.getAccessFlags());
132     }
133 
134     /**
135      * Sets the encoded modifiers of the method/constructor.
136      *
137      * <p>Changing the modifiers may cause a problem.
138      * For example, if a non-static method is changed to static,
139      * the method will be rejected by the bytecode verifier.
140      *
141      * @see Modifier
142      */
setModifiers(int mod)143     public void setModifiers(int mod) {
144         declaringClass.checkModify();
145         methodInfo.setAccessFlags(AccessFlag.of(mod));
146     }
147 
148     /**
149      * Returns true if the class has the specified annotation class.
150      *
151      * @param clz the annotation class.
152      * @return <code>true</code> if the annotation is found,
153      *         otherwise <code>false</code>.
154      * @since 3.11
155      */
hasAnnotation(Class clz)156     public boolean hasAnnotation(Class clz) {
157        MethodInfo mi = getMethodInfo2();
158        AnnotationsAttribute ainfo = (AnnotationsAttribute)
159                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
160        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
161                    mi.getAttribute(AnnotationsAttribute.visibleTag);
162        return CtClassType.hasAnnotationType(clz,
163                                             getDeclaringClass().getClassPool(),
164                                             ainfo, ainfo2);
165     }
166 
167     /**
168      * Returns the annotation if the class has the specified annotation class.
169      * For example, if an annotation <code>@Author</code> is associated
170      * with this method/constructor, an <code>Author</code> object is returned.
171      * The member values can be obtained by calling methods on
172      * the <code>Author</code> object.
173      *
174      * @param clz the annotation class.
175      * @return the annotation if found, otherwise <code>null</code>.
176      * @since 3.11
177      */
getAnnotation(Class clz)178     public Object getAnnotation(Class clz) throws ClassNotFoundException {
179        MethodInfo mi = getMethodInfo2();
180        AnnotationsAttribute ainfo = (AnnotationsAttribute)
181                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
182        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
183                    mi.getAttribute(AnnotationsAttribute.visibleTag);
184        return CtClassType.getAnnotationType(clz,
185                                             getDeclaringClass().getClassPool(),
186                                             ainfo, ainfo2);
187     }
188 
189     /**
190      * Returns the annotations associated with this method or constructor.
191      *
192      * @return an array of annotation-type objects.
193      * @see #getAvailableAnnotations()
194      * @since 3.1
195      */
getAnnotations()196     public Object[] getAnnotations() throws ClassNotFoundException {
197        return getAnnotations(false);
198    }
199 
200     /**
201      * Returns the annotations associated with this method or constructor.
202      * If any annotations are not on the classpath, they are not included
203      * in the returned array.
204      *
205      * @return an array of annotation-type objects.
206      * @see #getAnnotations()
207      * @since 3.3
208      */
getAvailableAnnotations()209     public Object[] getAvailableAnnotations(){
210        try{
211            return getAnnotations(true);
212        }
213        catch (ClassNotFoundException e){
214            throw new RuntimeException("Unexpected exception", e);
215        }
216     }
217 
getAnnotations(boolean ignoreNotFound)218     private Object[] getAnnotations(boolean ignoreNotFound)
219        throws ClassNotFoundException
220     {
221        MethodInfo mi = getMethodInfo2();
222        AnnotationsAttribute ainfo = (AnnotationsAttribute)
223                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
224        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
225                    mi.getAttribute(AnnotationsAttribute.visibleTag);
226        return CtClassType.toAnnotationType(ignoreNotFound,
227                                            getDeclaringClass().getClassPool(),
228                                            ainfo, ainfo2);
229     }
230 
231     /**
232      * Returns the parameter annotations associated with this method or constructor.
233      *
234      * @return an array of annotation-type objects.  The length of the returned array is
235      * equal to the number of the formal parameters.  If each parameter has no
236      * annotation, the elements of the returned array are empty arrays.
237      *
238      * @see #getAvailableParameterAnnotations()
239      * @see #getAnnotations()
240      * @since 3.1
241      */
getParameterAnnotations()242     public Object[][] getParameterAnnotations() throws ClassNotFoundException {
243         return getParameterAnnotations(false);
244     }
245 
246     /**
247      * Returns the parameter annotations associated with this method or constructor.
248      * If any annotations are not on the classpath, they are not included in the
249      * returned array.
250      *
251      * @return an array of annotation-type objects.  The length of the returned array is
252      * equal to the number of the formal parameters.  If each parameter has no
253      * annotation, the elements of the returned array are empty arrays.
254      *
255      * @see #getParameterAnnotations()
256      * @see #getAvailableAnnotations()
257      * @since 3.3
258      */
getAvailableParameterAnnotations()259     public Object[][] getAvailableParameterAnnotations(){
260         try {
261             return getParameterAnnotations(true);
262         }
263         catch(ClassNotFoundException e) {
264             throw new RuntimeException("Unexpected exception", e);
265         }
266     }
267 
getParameterAnnotations(boolean ignoreNotFound)268     Object[][] getParameterAnnotations(boolean ignoreNotFound)
269         throws ClassNotFoundException
270     {
271         MethodInfo mi = getMethodInfo2();
272         ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute)
273                     mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
274         ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute)
275                     mi.getAttribute(ParameterAnnotationsAttribute.visibleTag);
276         return CtClassType.toAnnotationType(ignoreNotFound,
277                                             getDeclaringClass().getClassPool(),
278                                             ainfo, ainfo2, mi);
279     }
280 
281     /**
282      * Obtains parameter types of this method/constructor.
283      */
getParameterTypes()284     public CtClass[] getParameterTypes() throws NotFoundException {
285         return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
286                                             declaringClass.getClassPool());
287     }
288 
289     /**
290      * Obtains the type of the returned value.
291      */
getReturnType0()292     CtClass getReturnType0() throws NotFoundException {
293         return Descriptor.getReturnType(methodInfo.getDescriptor(),
294                                         declaringClass.getClassPool());
295     }
296 
297     /**
298      * Returns the method signature (the parameter types
299      * and the return type).
300      * The method signature is represented by a character string
301      * called method descriptor, which is defined in the JVM specification.
302      * If two methods/constructors have
303      * the same parameter types
304      * and the return type, <code>getSignature()</code> returns the
305      * same string (the return type of constructors is <code>void</code>).
306      *
307      * <p>Note that the returned string is not the type signature
308      * contained in the <code>SignatureAttirbute</code>.  It is
309      * a descriptor.  To obtain a type signature, call the following
310      * methods:
311      *
312      * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag)
313      * </pre></ul>
314      *
315      * @see javassist.bytecode.Descriptor
316      * @see javassist.bytecode.SignatureAttribute
317      */
getSignature()318     public String getSignature() {
319         return methodInfo.getDescriptor();
320     }
321 
322     /**
323      * Obtains exceptions that this method/constructor may throw.
324      *
325      * @return a zero-length array if there is no throws clause.
326      */
getExceptionTypes()327     public CtClass[] getExceptionTypes() throws NotFoundException {
328         String[] exceptions;
329         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
330         if (ea == null)
331             exceptions = null;
332         else
333             exceptions = ea.getExceptions();
334 
335         return declaringClass.getClassPool().get(exceptions);
336     }
337 
338     /**
339      * Sets exceptions that this method/constructor may throw.
340      */
setExceptionTypes(CtClass[] types)341     public void setExceptionTypes(CtClass[] types) throws NotFoundException {
342         declaringClass.checkModify();
343         if (types == null || types.length == 0) {
344             methodInfo.removeExceptionsAttribute();
345             return;
346         }
347 
348         String[] names = new String[types.length];
349         for (int i = 0; i < types.length; ++i)
350             names[i] = types[i].getName();
351 
352         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
353         if (ea == null) {
354             ea = new ExceptionsAttribute(methodInfo.getConstPool());
355             methodInfo.setExceptionsAttribute(ea);
356         }
357 
358         ea.setExceptions(names);
359     }
360 
361     /**
362      * Returns true if the body is empty.
363      */
isEmpty()364     public abstract boolean isEmpty();
365 
366     /**
367      * Sets a method/constructor body.
368      *
369      * @param src       the source code representing the body.
370      *                  It must be a single statement or block.
371      *                  If it is <code>null</code>, the substituted
372      *                  body does nothing except returning zero or null.
373      */
setBody(String src)374     public void setBody(String src) throws CannotCompileException {
375         setBody(src, null, null);
376     }
377 
378     /**
379      * Sets a method/constructor body.
380      *
381      * @param src       the source code representing the body.
382      *                  It must be a single statement or block.
383      *                  If it is <code>null</code>, the substituted
384      *                  body does nothing except returning zero or null.
385      * @param delegateObj       the source text specifying the object
386      *                          that is called on by <code>$proceed()</code>.
387      * @param delegateMethod    the name of the method
388      *                          that is called by <code>$proceed()</code>.
389      */
setBody(String src, String delegateObj, String delegateMethod)390     public void setBody(String src,
391                         String delegateObj, String delegateMethod)
392         throws CannotCompileException
393     {
394         CtClass cc = declaringClass;
395         cc.checkModify();
396         try {
397             Javac jv = new Javac(cc);
398             if (delegateMethod != null)
399                 jv.recordProceed(delegateObj, delegateMethod);
400 
401             Bytecode b = jv.compileBody(this, src);
402             methodInfo.setCodeAttribute(b.toCodeAttribute());
403             methodInfo.setAccessFlags(methodInfo.getAccessFlags()
404                                       & ~AccessFlag.ABSTRACT);
405             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
406             declaringClass.rebuildClassFile();
407         }
408         catch (CompileError e) {
409             throw new CannotCompileException(e);
410         } catch (BadBytecode e) {
411             throw new CannotCompileException(e);
412         }
413     }
414 
setBody0(CtClass srcClass, MethodInfo srcInfo, CtClass destClass, MethodInfo destInfo, ClassMap map)415     static void setBody0(CtClass srcClass, MethodInfo srcInfo,
416                          CtClass destClass, MethodInfo destInfo,
417                          ClassMap map)
418         throws CannotCompileException
419     {
420         destClass.checkModify();
421 
422         map = new ClassMap(map);
423         map.put(srcClass.getName(), destClass.getName());
424         try {
425             CodeAttribute cattr = srcInfo.getCodeAttribute();
426             if (cattr != null) {
427                 ConstPool cp = destInfo.getConstPool();
428                 CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
429                 destInfo.setCodeAttribute(ca);
430                 // a stack map table is copied to destInfo.
431             }
432         }
433         catch (CodeAttribute.RuntimeCopyException e) {
434             /* the exception may be thrown by copy() in CodeAttribute.
435              */
436             throw new CannotCompileException(e);
437         }
438 
439         destInfo.setAccessFlags(destInfo.getAccessFlags()
440                                 & ~AccessFlag.ABSTRACT);
441         destClass.rebuildClassFile();
442     }
443 
444     /**
445      * Obtains an attribute with the given name.
446      * If that attribute is not found in the class file, this
447      * method returns null.
448      *
449      * <p>Note that an attribute is a data block specified by
450      * the class file format.  It is not an annotation.
451      * See {@link javassist.bytecode.AttributeInfo}.
452      *
453      * @param name              attribute name
454      */
getAttribute(String name)455     public byte[] getAttribute(String name) {
456         AttributeInfo ai = methodInfo.getAttribute(name);
457         if (ai == null)
458             return null;
459         else
460             return ai.get();
461     }
462 
463     /**
464      * Adds an attribute. The attribute is saved in the class file.
465      *
466      * <p>Note that an attribute is a data block specified by
467      * the class file format.  It is not an annotation.
468      * See {@link javassist.bytecode.AttributeInfo}.
469      *
470      * @param name      attribute name
471      * @param data      attribute value
472      */
setAttribute(String name, byte[] data)473     public void setAttribute(String name, byte[] data) {
474         declaringClass.checkModify();
475         methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
476                                                   name, data));
477     }
478 
479     /**
480      * Declares to use <code>$cflow</code> for this method/constructor.
481      * If <code>$cflow</code> is used, the class files modified
482      * with Javassist requires a support class
483      * <code>javassist.runtime.Cflow</code> at runtime
484      * (other Javassist classes are not required at runtime).
485      *
486      * <p>Every <code>$cflow</code> variable is given a unique name.
487      * For example, if the given name is <code>"Point.paint"</code>,
488      * then the variable is indicated by <code>$cflow(Point.paint)</code>.
489      *
490      * @param name      <code>$cflow</code> name.  It can include
491      *                  alphabets, numbers, <code>_</code>,
492      *                  <code>$</code>, and <code>.</code> (dot).
493      *
494      * @see javassist.runtime.Cflow
495      */
useCflow(String name)496     public void useCflow(String name) throws CannotCompileException {
497         CtClass cc = declaringClass;
498         cc.checkModify();
499         ClassPool pool = cc.getClassPool();
500         String fname;
501         int i = 0;
502         while (true) {
503             fname = "_cflow$" + i++;
504             try {
505                 cc.getDeclaredField(fname);
506             }
507             catch(NotFoundException e) {
508                 break;
509             }
510         }
511 
512         pool.recordCflow(name, declaringClass.getName(), fname);
513         try {
514             CtClass type = pool.get("javassist.runtime.Cflow");
515             CtField field = new CtField(type, fname, cc);
516             field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
517             cc.addField(field, CtField.Initializer.byNew(type));
518             insertBefore(fname + ".enter();", false);
519             String src = fname + ".exit();";
520             insertAfter(src, true);
521         }
522         catch (NotFoundException e) {
523             throw new CannotCompileException(e);
524         }
525     }
526 
527     /**
528      * Declares a new local variable.  The scope of this variable is the
529      * whole method body.  The initial value of that variable is not set.
530      * The declared variable can be accessed in the code snippet inserted
531      * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
532      *
533      * <p>If the second parameter <code>asFinally</code> to
534      * <code>insertAfter()</code> is true, the declared local variable
535      * is not visible from the code inserted by <code>insertAfter()</code>.
536      *
537      * @param name      the name of the variable
538      * @param type      the type of the variable
539      * @see #insertBefore(String)
540      * @see #insertAfter(String)
541      */
addLocalVariable(String name, CtClass type)542     public void addLocalVariable(String name, CtClass type)
543         throws CannotCompileException
544     {
545         declaringClass.checkModify();
546         ConstPool cp = methodInfo.getConstPool();
547         CodeAttribute ca = methodInfo.getCodeAttribute();
548         if (ca == null)
549             throw new CannotCompileException("no method body");
550 
551         LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(
552                                                 LocalVariableAttribute.tag);
553         if (va == null) {
554             va = new LocalVariableAttribute(cp);
555             ca.getAttributes().add(va);
556         }
557 
558         int maxLocals = ca.getMaxLocals();
559         String desc = Descriptor.of(type);
560         va.addEntry(0, ca.getCodeLength(),
561                     cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals);
562         ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
563     }
564 
565     /**
566      * Inserts a new parameter, which becomes the first parameter.
567      */
insertParameter(CtClass type)568     public void insertParameter(CtClass type)
569         throws CannotCompileException
570     {
571         declaringClass.checkModify();
572         String desc = methodInfo.getDescriptor();
573         String desc2 = Descriptor.insertParameter(type, desc);
574         try {
575             addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc);
576         }
577         catch (BadBytecode e) {
578             throw new CannotCompileException(e);
579         }
580 
581         methodInfo.setDescriptor(desc2);
582     }
583 
584     /**
585      * Appends a new parameter, which becomes the last parameter.
586      */
addParameter(CtClass type)587     public void addParameter(CtClass type)
588         throws CannotCompileException
589     {
590         declaringClass.checkModify();
591         String desc = methodInfo.getDescriptor();
592         String desc2 = Descriptor.appendParameter(type, desc);
593         int offset = Modifier.isStatic(getModifiers()) ? 0 : 1;
594         try {
595             addParameter2(offset + Descriptor.paramSize(desc), type, desc);
596         }
597         catch (BadBytecode e) {
598             throw new CannotCompileException(e);
599         }
600 
601         methodInfo.setDescriptor(desc2);
602     }
603 
addParameter2(int where, CtClass type, String desc)604     private void addParameter2(int where, CtClass type, String desc)
605         throws BadBytecode
606     {
607         CodeAttribute ca = methodInfo.getCodeAttribute();
608         if (ca != null) {
609             int size = 1;
610             char typeDesc = 'L';
611             int classInfo = 0;
612             if (type.isPrimitive()) {
613                 CtPrimitiveType cpt = (CtPrimitiveType)type;
614                 size = cpt.getDataSize();
615                 typeDesc = cpt.getDescriptor();
616             }
617             else
618                 classInfo = methodInfo.getConstPool().addClassInfo(type);
619 
620             ca.insertLocalVar(where, size);
621             LocalVariableAttribute va
622                             = (LocalVariableAttribute)
623                               ca.getAttribute(LocalVariableAttribute.tag);
624             if (va != null)
625                 va.shiftIndex(where, size);
626 
627             StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
628             if (smt != null)
629                 smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
630 
631             StackMap sm = (StackMap)ca.getAttribute(StackMap.tag);
632             if (sm != null)
633                 sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
634         }
635     }
636 
637     /**
638      * Modifies the method/constructor body.
639      *
640      * @param converter         specifies how to modify.
641      */
instrument(CodeConverter converter)642     public void instrument(CodeConverter converter)
643         throws CannotCompileException
644     {
645         declaringClass.checkModify();
646         ConstPool cp = methodInfo.getConstPool();
647         converter.doit(getDeclaringClass(), methodInfo, cp);
648     }
649 
650     /**
651      * Modifies the method/constructor body.
652      *
653      * @param editor            specifies how to modify.
654      */
instrument(ExprEditor editor)655     public void instrument(ExprEditor editor)
656         throws CannotCompileException
657     {
658         // if the class is not frozen,
659         // does not turn the modified flag on.
660         if (declaringClass.isFrozen())
661             declaringClass.checkModify();
662 
663         if (editor.doit(declaringClass, methodInfo))
664             declaringClass.checkModify();
665     }
666 
667     /**
668      * Inserts bytecode at the beginning of the body.
669      *
670      * <p>If this object represents a constructor,
671      * the bytecode is inserted before
672      * a constructor in the super class or this class is called.
673      * Therefore, the inserted bytecode is subject to constraints described
674      * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
675      * For example, it cannot access instance fields or methods although
676      * it may assign a value to an instance field directly declared in this
677      * class.  Accessing static fields and methods is allowed.
678      * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
679      *
680      * @param src       the source code representing the inserted bytecode.
681      *                  It must be a single statement or block.
682      * @see CtConstructor#insertBeforeBody(String)
683      */
insertBefore(String src)684     public void insertBefore(String src) throws CannotCompileException {
685         insertBefore(src, true);
686     }
687 
insertBefore(String src, boolean rebuild)688     private void insertBefore(String src, boolean rebuild)
689         throws CannotCompileException
690     {
691         CtClass cc = declaringClass;
692         cc.checkModify();
693         CodeAttribute ca = methodInfo.getCodeAttribute();
694         if (ca == null)
695             throw new CannotCompileException("no method body");
696 
697         CodeIterator iterator = ca.iterator();
698         Javac jv = new Javac(cc);
699         try {
700             int nvars = jv.recordParams(getParameterTypes(),
701                                         Modifier.isStatic(getModifiers()));
702             jv.recordParamNames(ca, nvars);
703             jv.recordLocalVariables(ca, 0);
704             jv.recordType(getReturnType0());
705             jv.compileStmnt(src);
706             Bytecode b = jv.getBytecode();
707             int stack = b.getMaxStack();
708             int locals = b.getMaxLocals();
709 
710             if (stack > ca.getMaxStack())
711                 ca.setMaxStack(stack);
712 
713             if (locals > ca.getMaxLocals())
714                 ca.setMaxLocals(locals);
715 
716             int pos = iterator.insertEx(b.get());
717             iterator.insert(b.getExceptionTable(), pos);
718             if (rebuild)
719                 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
720         }
721         catch (NotFoundException e) {
722             throw new CannotCompileException(e);
723         }
724         catch (CompileError e) {
725             throw new CannotCompileException(e);
726         }
727         catch (BadBytecode e) {
728             throw new CannotCompileException(e);
729         }
730     }
731 
732     /**
733      * Inserts bytecode at the end of the body.
734      * The bytecode is inserted just before every return insturction.
735      * It is not executed when an exception is thrown.
736      *
737      * @param src       the source code representing the inserted bytecode.
738      *                  It must be a single statement or block.
739      */
insertAfter(String src)740     public void insertAfter(String src)
741         throws CannotCompileException
742     {
743         insertAfter(src, false);
744     }
745 
746     /**
747      * Inserts bytecode at the end of the body.
748      * The bytecode is inserted just before every return insturction.
749      *
750      * @param src       the source code representing the inserted bytecode.
751      *                  It must be a single statement or block.
752      * @param asFinally         true if the inserted bytecode is executed
753      *                  not only when the control normally returns
754      *                  but also when an exception is thrown.
755      *                  If this parameter is true, the inserted code cannot
756      *                  access local variables.
757      */
insertAfter(String src, boolean asFinally)758     public void insertAfter(String src, boolean asFinally)
759         throws CannotCompileException
760     {
761         CtClass cc = declaringClass;
762         cc.checkModify();
763         ConstPool pool = methodInfo.getConstPool();
764         CodeAttribute ca = methodInfo.getCodeAttribute();
765         if (ca == null)
766             throw new CannotCompileException("no method body");
767 
768         CodeIterator iterator = ca.iterator();
769         int retAddr = ca.getMaxLocals();
770         Bytecode b = new Bytecode(pool, 0, retAddr + 1);
771         b.setStackDepth(ca.getMaxStack() + 1);
772         Javac jv = new Javac(b, cc);
773         try {
774             int nvars = jv.recordParams(getParameterTypes(),
775                                         Modifier.isStatic(getModifiers()));
776             jv.recordParamNames(ca, nvars);
777             CtClass rtype = getReturnType0();
778             int varNo = jv.recordReturnType(rtype, true);
779             jv.recordLocalVariables(ca, 0);
780 
781             // finally clause for exceptions
782             int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
783                                                 jv, src);
784             // finally clause for normal termination
785             insertAfterAdvice(b, jv, src, pool, rtype, varNo);
786 
787             ca.setMaxStack(b.getMaxStack());
788             ca.setMaxLocals(b.getMaxLocals());
789 
790             int gapPos = iterator.append(b.get());
791             iterator.append(b.getExceptionTable(), gapPos);
792 
793             if (asFinally)
794                 ca.getExceptionTable().add(getStartPosOfBody(ca), gapPos, gapPos, 0);
795 
796             int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
797             int subr = iterator.getCodeLength() - gapLen;
798 
799             while (iterator.hasNext()) {
800                 int pos = iterator.next();
801                 if (pos >= subr)
802                     break;
803 
804                 int c = iterator.byteAt(pos);
805                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
806                     || c == Opcode.FRETURN || c == Opcode.LRETURN
807                     || c == Opcode.DRETURN || c == Opcode.RETURN) {
808                     insertGoto(iterator, subr, pos);
809                     subr = iterator.getCodeLength() - gapLen;
810                 }
811             }
812 
813             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
814         }
815         catch (NotFoundException e) {
816             throw new CannotCompileException(e);
817         }
818         catch (CompileError e) {
819             throw new CannotCompileException(e);
820         }
821         catch (BadBytecode e) {
822             throw new CannotCompileException(e);
823         }
824     }
825 
insertAfterAdvice(Bytecode code, Javac jv, String src, ConstPool cp, CtClass rtype, int varNo)826     private void insertAfterAdvice(Bytecode code, Javac jv, String src,
827                                    ConstPool cp, CtClass rtype, int varNo)
828         throws CompileError
829     {
830         if (rtype == CtClass.voidType) {
831             code.addOpcode(Opcode.ACONST_NULL);
832             code.addAstore(varNo);
833             jv.compileStmnt(src);
834             code.addOpcode(Opcode.RETURN);
835             if (code.getMaxLocals() < 1)
836                 code.setMaxLocals(1);
837         }
838         else {
839             code.addStore(varNo, rtype);
840             jv.compileStmnt(src);
841             code.addLoad(varNo, rtype);
842             if (rtype.isPrimitive())
843                 code.addOpcode(((CtPrimitiveType)rtype).getReturnOp());
844             else
845                 code.addOpcode(Opcode.ARETURN);
846         }
847     }
848 
849     /*
850      * assert subr > pos
851      */
insertGoto(CodeIterator iterator, int subr, int pos)852     private void insertGoto(CodeIterator iterator, int subr, int pos)
853         throws BadBytecode
854     {
855         iterator.setMark(subr);
856         // the gap length might be a multiple of 4.
857         iterator.writeByte(Opcode.NOP, pos);
858         boolean wide = subr + 2 - pos > Short.MAX_VALUE;
859         pos = iterator.insertGapAt(pos, wide ? 4 : 2, false).position;
860         int offset = iterator.getMark() - pos;
861         if (wide) {
862             iterator.writeByte(Opcode.GOTO_W, pos);
863             iterator.write32bit(offset, pos + 1);
864         }
865         else if (offset <= Short.MAX_VALUE) {
866             iterator.writeByte(Opcode.GOTO, pos);
867             iterator.write16bit(offset, pos + 1);
868         }
869         else {
870             pos = iterator.insertGapAt(pos, 2, false).position;
871             iterator.writeByte(Opcode.GOTO_W, pos);
872             iterator.write32bit(iterator.getMark() - pos, pos + 1);
873         }
874     }
875 
876     /* insert a finally clause
877      */
insertAfterHandler(boolean asFinally, Bytecode b, CtClass rtype, int returnVarNo, Javac javac, String src)878     private int insertAfterHandler(boolean asFinally, Bytecode b,
879                                    CtClass rtype, int returnVarNo,
880                                    Javac javac, String src)
881         throws CompileError
882     {
883         if (!asFinally)
884             return 0;
885 
886         int var = b.getMaxLocals();
887         b.incMaxLocals(1);
888         int pc = b.currentPc();
889         b.addAstore(var);   // store an exception
890         if (rtype.isPrimitive()) {
891             char c = ((CtPrimitiveType)rtype).getDescriptor();
892             if (c == 'D') {
893                 b.addDconst(0.0);
894                 b.addDstore(returnVarNo);
895             }
896             else if (c == 'F') {
897                 b.addFconst(0);
898                 b.addFstore(returnVarNo);
899             }
900             else if (c == 'J') {
901                 b.addLconst(0);
902                 b.addLstore(returnVarNo);
903             }
904             else if (c == 'V') {
905                 b.addOpcode(Opcode.ACONST_NULL);
906                 b.addAstore(returnVarNo);
907             }
908             else { // int, boolean, char, short, ...
909                 b.addIconst(0);
910                 b.addIstore(returnVarNo);
911             }
912         }
913         else {
914             b.addOpcode(Opcode.ACONST_NULL);
915             b.addAstore(returnVarNo);
916         }
917 
918         javac.compileStmnt(src);
919         b.addAload(var);
920         b.addOpcode(Opcode.ATHROW);
921         return b.currentPc() - pc;
922     }
923 
924     /* -- OLD version --
925 
926     public void insertAfter(String src) throws CannotCompileException {
927         declaringClass.checkModify();
928         CodeAttribute ca = methodInfo.getCodeAttribute();
929         CodeIterator iterator = ca.iterator();
930         Bytecode b = new Bytecode(methodInfo.getConstPool(),
931                                   ca.getMaxStack(), ca.getMaxLocals());
932         b.setStackDepth(ca.getMaxStack());
933         Javac jv = new Javac(b, declaringClass);
934         try {
935             jv.recordParams(getParameterTypes(),
936                             Modifier.isStatic(getModifiers()));
937             CtClass rtype = getReturnType0();
938             int varNo = jv.recordReturnType(rtype, true);
939             boolean isVoid = rtype == CtClass.voidType;
940             if (isVoid) {
941                 b.addOpcode(Opcode.ACONST_NULL);
942                 b.addAstore(varNo);
943                 jv.compileStmnt(src);
944             }
945             else {
946                 b.addStore(varNo, rtype);
947                 jv.compileStmnt(src);
948                 b.addLoad(varNo, rtype);
949             }
950 
951             byte[] code = b.get();
952             ca.setMaxStack(b.getMaxStack());
953             ca.setMaxLocals(b.getMaxLocals());
954             while (iterator.hasNext()) {
955                 int pos = iterator.next();
956                 int c = iterator.byteAt(pos);
957                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
958                     || c == Opcode.FRETURN || c == Opcode.LRETURN
959                     || c == Opcode.DRETURN || c == Opcode.RETURN)
960                     iterator.insert(pos, code);
961             }
962         }
963         catch (NotFoundException e) {
964             throw new CannotCompileException(e);
965         }
966         catch (CompileError e) {
967             throw new CannotCompileException(e);
968         }
969         catch (BadBytecode e) {
970             throw new CannotCompileException(e);
971         }
972     }
973     */
974 
975     /**
976      * Adds a catch clause that handles an exception thrown in the
977      * body.  The catch clause must end with a return or throw statement.
978      *
979      * @param src       the source code representing the catch clause.
980      *                  It must be a single statement or block.
981      * @param exceptionType     the type of the exception handled by the
982      *                          catch clause.
983      */
addCatch(String src, CtClass exceptionType)984     public void addCatch(String src, CtClass exceptionType)
985         throws CannotCompileException
986     {
987         addCatch(src, exceptionType, "$e");
988     }
989 
990     /**
991      * Adds a catch clause that handles an exception thrown in the
992      * body.  The catch clause must end with a return or throw statement.
993      *
994      * @param src       the source code representing the catch clause.
995      *                  It must be a single statement or block.
996      * @param exceptionType     the type of the exception handled by the
997      *                          catch clause.
998      * @param exceptionName     the name of the variable containing the
999      *                          caught exception, for example,
1000      *                          <code>$e</code>.
1001      */
addCatch(String src, CtClass exceptionType, String exceptionName)1002     public void addCatch(String src, CtClass exceptionType,
1003                          String exceptionName)
1004         throws CannotCompileException
1005     {
1006         CtClass cc = declaringClass;
1007         cc.checkModify();
1008         ConstPool cp = methodInfo.getConstPool();
1009         CodeAttribute ca = methodInfo.getCodeAttribute();
1010         CodeIterator iterator = ca.iterator();
1011         Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
1012         b.setStackDepth(1);
1013         Javac jv = new Javac(b, cc);
1014         try {
1015             jv.recordParams(getParameterTypes(),
1016                             Modifier.isStatic(getModifiers()));
1017             int var = jv.recordVariable(exceptionType, exceptionName);
1018             b.addAstore(var);
1019             jv.compileStmnt(src);
1020 
1021             int stack = b.getMaxStack();
1022             int locals = b.getMaxLocals();
1023 
1024             if (stack > ca.getMaxStack())
1025                 ca.setMaxStack(stack);
1026 
1027             if (locals > ca.getMaxLocals())
1028                 ca.setMaxLocals(locals);
1029 
1030             int len = iterator.getCodeLength();
1031             int pos = iterator.append(b.get());
1032             ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
1033                                        cp.addClassInfo(exceptionType));
1034             iterator.append(b.getExceptionTable(), pos);
1035             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
1036         }
1037         catch (NotFoundException e) {
1038             throw new CannotCompileException(e);
1039         }
1040         catch (CompileError e) {
1041             throw new CannotCompileException(e);
1042         } catch (BadBytecode e) {
1043             throw new CannotCompileException(e);
1044         }
1045     }
1046 
1047     /* CtConstructor overrides this method.
1048      */
getStartPosOfBody(CodeAttribute ca)1049     int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
1050         return 0;
1051     }
1052 
1053     /**
1054      * Inserts bytecode at the specified line in the body.
1055      * It is equivalent to:
1056      *
1057      * <br><code>insertAt(lineNum, true, src)</code>
1058      *
1059      * <br>See this method as well.
1060      *
1061      * @param lineNum   the line number.  The bytecode is inserted at the
1062      *                  beginning of the code at the line specified by this
1063      *                  line number.
1064      * @param src       the source code representing the inserted bytecode.
1065      *                  It must be a single statement or block.
1066      * @return      the line number at which the bytecode has been inserted.
1067      *
1068      * @see CtBehavior#insertAt(int,boolean,String)
1069      */
insertAt(int lineNum, String src)1070     public int insertAt(int lineNum, String src)
1071         throws CannotCompileException
1072     {
1073         return insertAt(lineNum, true, src);
1074     }
1075 
1076     /**
1077      * Inserts bytecode at the specified line in the body.
1078      *
1079      * <p>If there is not
1080      * a statement at the specified line, the bytecode might be inserted
1081      * at the line including the first statement after that line specified.
1082      * For example, if there is only a closing brace at that line, the
1083      * bytecode would be inserted at another line below.
1084      * To know exactly where the bytecode will be inserted, call with
1085      * <code>modify</code> set to <code>false</code>.
1086      *
1087      * @param lineNum   the line number.  The bytecode is inserted at the
1088      *                  beginning of the code at the line specified by this
1089      *                  line number.
1090      * @param modify    if false, this method does not insert the bytecode.
1091      *                  It instead only returns the line number at which
1092      *                  the bytecode would be inserted.
1093      * @param src       the source code representing the inserted bytecode.
1094      *                  It must be a single statement or block.
1095      *                  If modify is false, the value of src can be null.
1096      * @return      the line number at which the bytecode has been inserted.
1097      */
insertAt(int lineNum, boolean modify, String src)1098     public int insertAt(int lineNum, boolean modify, String src)
1099         throws CannotCompileException
1100     {
1101         CodeAttribute ca = methodInfo.getCodeAttribute();
1102         if (ca == null)
1103             throw new CannotCompileException("no method body");
1104 
1105         LineNumberAttribute ainfo
1106             = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
1107         if (ainfo == null)
1108             throw new CannotCompileException("no line number info");
1109 
1110         LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
1111         lineNum = pc.line;
1112         int index = pc.index;
1113         if (!modify)
1114             return lineNum;
1115 
1116         CtClass cc = declaringClass;
1117         cc.checkModify();
1118         CodeIterator iterator = ca.iterator();
1119         Javac jv = new Javac(cc);
1120         try {
1121             jv.recordLocalVariables(ca, index);
1122             jv.recordParams(getParameterTypes(),
1123                             Modifier.isStatic(getModifiers()));
1124             jv.setMaxLocals(ca.getMaxLocals());
1125             jv.compileStmnt(src);
1126             Bytecode b = jv.getBytecode();
1127             int locals = b.getMaxLocals();
1128             int stack = b.getMaxStack();
1129             ca.setMaxLocals(locals);
1130 
1131             /* We assume that there is no values in the operand stack
1132              * at the position where the bytecode is inserted.
1133              */
1134             if (stack > ca.getMaxStack())
1135                 ca.setMaxStack(stack);
1136 
1137             index = iterator.insertAt(index, b.get());
1138             iterator.insert(b.getExceptionTable(), index);
1139             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
1140             return lineNum;
1141         }
1142         catch (NotFoundException e) {
1143             throw new CannotCompileException(e);
1144         }
1145         catch (CompileError e) {
1146             throw new CannotCompileException(e);
1147         }
1148         catch (BadBytecode e) {
1149             throw new CannotCompileException(e);
1150         }
1151     }
1152 }
1153