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