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
16package javassist;
17
18import javassist.bytecode.*;
19import javassist.compiler.Javac;
20import javassist.compiler.CompileError;
21import 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 */
29public abstract class CtBehavior extends CtMember {
30    protected MethodInfo methodInfo;
31
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     */
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
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     */
91    public abstract String getLongName();
92
93    /**
94     * Returns the MethodInfo representing this method/constructor in the
95     * class file.
96     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
684    public void insertBefore(String src) throws CannotCompileException {
685        insertBefore(src, true);
686    }
687
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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