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.bytecode;
17
18import java.io.DataInputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.List;
23import java.util.Map;
24import javassist.ClassPool;
25import javassist.bytecode.stackmap.MapMaker;
26
27/**
28 * <code>method_info</code> structure.
29 *
30 * @see javassist.CtMethod#getMethodInfo()
31 * @see javassist.CtConstructor#getMethodInfo()
32 */
33public class MethodInfo {
34    ConstPool constPool;
35    int accessFlags;
36    int name;
37    String cachedName;
38    int descriptor;
39    ArrayList attribute; // may be null
40
41    /**
42     * If this value is true, Javassist maintains a <code>StackMap</code> attribute
43     * generated by the <code>preverify</code> tool of J2ME (CLDC).  The initial
44     * value of this field is <code>false</code>.
45     */
46    public static boolean doPreverify = false;
47
48    /**
49     * The name of constructors: <code>&lt;init&gt</code>.
50     */
51    public static final String nameInit = "<init>";
52
53    /**
54     * The name of class initializer (static initializer):
55     * <code>&lt;clinit&gt</code>.
56     */
57    public static final String nameClinit = "<clinit>";
58
59    private MethodInfo(ConstPool cp) {
60        constPool = cp;
61        attribute = null;
62    }
63
64    /**
65     * Constructs a <code>method_info</code> structure. The initial value of
66     * <code>access_flags</code> is zero.
67     *
68     * @param cp
69     *            a constant pool table
70     * @param methodname
71     *            method name
72     * @param desc
73     *            method descriptor
74     * @see Descriptor
75     */
76    public MethodInfo(ConstPool cp, String methodname, String desc) {
77        this(cp);
78        accessFlags = 0;
79        name = cp.addUtf8Info(methodname);
80        cachedName = methodname;
81        descriptor = constPool.addUtf8Info(desc);
82    }
83
84    MethodInfo(ConstPool cp, DataInputStream in) throws IOException {
85        this(cp);
86        read(in);
87    }
88
89    /**
90     * Constructs a copy of <code>method_info</code> structure. Class names
91     * appearing in the source <code>method_info</code> are renamed according
92     * to <code>classnameMap</code>.
93     *
94     * <p>
95     * Note: only <code>Code</code> and <code>Exceptions</code> attributes
96     * are copied from the source. The other attributes are ignored.
97     *
98     * @param cp
99     *            a constant pool table
100     * @param methodname
101     *            a method name
102     * @param src
103     *            a source <code>method_info</code>
104     * @param classnameMap
105     *            specifies pairs of replaced and substituted name.
106     * @see Descriptor
107     */
108    public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
109            Map classnameMap) throws BadBytecode {
110        this(cp);
111        read(src, methodname, classnameMap);
112    }
113
114    /**
115     * Returns a string representation of the object.
116     */
117    public String toString() {
118        return getName() + " " + getDescriptor();
119    }
120
121    /**
122     * Copies all constant pool items to a given new constant pool
123     * and replaces the original items with the new ones.
124     * This is used for garbage collecting the items of removed fields
125     * and methods.
126     *
127     * @param cp    the destination
128     */
129    void compact(ConstPool cp) {
130        name = cp.addUtf8Info(getName());
131        descriptor = cp.addUtf8Info(getDescriptor());
132        attribute = AttributeInfo.copyAll(attribute, cp);
133        constPool = cp;
134    }
135
136    void prune(ConstPool cp) {
137        ArrayList newAttributes = new ArrayList();
138
139        AttributeInfo invisibleAnnotations
140            = getAttribute(AnnotationsAttribute.invisibleTag);
141        if (invisibleAnnotations != null) {
142            invisibleAnnotations = invisibleAnnotations.copy(cp, null);
143            newAttributes.add(invisibleAnnotations);
144        }
145
146        AttributeInfo visibleAnnotations
147            = getAttribute(AnnotationsAttribute.visibleTag);
148        if (visibleAnnotations != null) {
149            visibleAnnotations = visibleAnnotations.copy(cp, null);
150            newAttributes.add(visibleAnnotations);
151        }
152
153        AttributeInfo parameterInvisibleAnnotations
154            = getAttribute(ParameterAnnotationsAttribute.invisibleTag);
155        if (parameterInvisibleAnnotations != null) {
156            parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null);
157            newAttributes.add(parameterInvisibleAnnotations);
158        }
159
160        AttributeInfo parameterVisibleAnnotations
161            = getAttribute(ParameterAnnotationsAttribute.visibleTag);
162        if (parameterVisibleAnnotations != null) {
163            parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null);
164            newAttributes.add(parameterVisibleAnnotations);
165        }
166
167        AnnotationDefaultAttribute defaultAttribute
168             = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag);
169        if (defaultAttribute != null)
170            newAttributes.add(defaultAttribute);
171
172        ExceptionsAttribute ea = getExceptionsAttribute();
173        if (ea != null)
174            newAttributes.add(ea);
175
176        AttributeInfo signature
177            = getAttribute(SignatureAttribute.tag);
178        if (signature != null) {
179            signature = signature.copy(cp, null);
180            newAttributes.add(signature);
181        }
182
183        attribute = newAttributes;
184        name = cp.addUtf8Info(getName());
185        descriptor = cp.addUtf8Info(getDescriptor());
186        constPool = cp;
187    }
188
189    /**
190     * Returns a method name.
191     */
192    public String getName() {
193       if (cachedName == null)
194           cachedName = constPool.getUtf8Info(name);
195
196       return cachedName;
197    }
198
199    /**
200     * Sets a method name.
201     */
202    public void setName(String newName) {
203        name = constPool.addUtf8Info(newName);
204        cachedName = newName;
205    }
206
207    /**
208     * Returns true if this is not a constructor or a class initializer (static
209     * initializer).
210     */
211    public boolean isMethod() {
212        String n = getName();
213        return !n.equals(nameInit) && !n.equals(nameClinit);
214    }
215
216    /**
217     * Returns a constant pool table used by this method.
218     */
219    public ConstPool getConstPool() {
220        return constPool;
221    }
222
223    /**
224     * Returns true if this is a constructor.
225     */
226    public boolean isConstructor() {
227        return getName().equals(nameInit);
228    }
229
230    /**
231     * Returns true if this is a class initializer (static initializer).
232     */
233    public boolean isStaticInitializer() {
234        return getName().equals(nameClinit);
235    }
236
237    /**
238     * Returns access flags.
239     *
240     * @see AccessFlag
241     */
242    public int getAccessFlags() {
243        return accessFlags;
244    }
245
246    /**
247     * Sets access flags.
248     *
249     * @see AccessFlag
250     */
251    public void setAccessFlags(int acc) {
252        accessFlags = acc;
253    }
254
255    /**
256     * Returns a method descriptor.
257     *
258     * @see Descriptor
259     */
260    public String getDescriptor() {
261        return constPool.getUtf8Info(descriptor);
262    }
263
264    /**
265     * Sets a method descriptor.
266     *
267     * @see Descriptor
268     */
269    public void setDescriptor(String desc) {
270        if (!desc.equals(getDescriptor()))
271            descriptor = constPool.addUtf8Info(desc);
272    }
273
274    /**
275     * Returns all the attributes.  The returned <code>List</code> object
276     * is shared with this object.  If you add a new attribute to the list,
277     * the attribute is also added to the method represented by this
278     * object.  If you remove an attribute from the list, it is also removed
279     * from the method.
280     *
281     * @return a list of <code>AttributeInfo</code> objects.
282     * @see AttributeInfo
283     */
284    public List getAttributes() {
285        if (attribute == null)
286            attribute = new ArrayList();
287
288        return attribute;
289    }
290
291    /**
292     * Returns the attribute with the specified name. If it is not found, this
293     * method returns null.
294     *
295     * @param name attribute name
296     * @return an <code>AttributeInfo</code> object or null.
297     * @see #getAttributes()
298     */
299    public AttributeInfo getAttribute(String name) {
300        return AttributeInfo.lookup(attribute, name);
301    }
302
303    /**
304     * Appends an attribute. If there is already an attribute with the same
305     * name, the new one substitutes for it.
306     *
307     * @see #getAttributes()
308     */
309    public void addAttribute(AttributeInfo info) {
310        if (attribute == null)
311            attribute = new ArrayList();
312
313        AttributeInfo.remove(attribute, info.getName());
314        attribute.add(info);
315    }
316
317    /**
318     * Returns an Exceptions attribute.
319     *
320     * @return an Exceptions attribute or null if it is not specified.
321     */
322    public ExceptionsAttribute getExceptionsAttribute() {
323        AttributeInfo info = AttributeInfo.lookup(attribute,
324                ExceptionsAttribute.tag);
325        return (ExceptionsAttribute)info;
326    }
327
328    /**
329     * Returns a Code attribute.
330     *
331     * @return a Code attribute or null if it is not specified.
332     */
333    public CodeAttribute getCodeAttribute() {
334        AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag);
335        return (CodeAttribute)info;
336    }
337
338    /**
339     * Removes an Exception attribute.
340     */
341    public void removeExceptionsAttribute() {
342        AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
343    }
344
345    /**
346     * Adds an Exception attribute.
347     *
348     * <p>
349     * The added attribute must share the same constant pool table as this
350     * <code>method_info</code> structure.
351     */
352    public void setExceptionsAttribute(ExceptionsAttribute cattr) {
353        removeExceptionsAttribute();
354        if (attribute == null)
355            attribute = new ArrayList();
356
357        attribute.add(cattr);
358    }
359
360    /**
361     * Removes a Code attribute.
362     */
363    public void removeCodeAttribute() {
364        AttributeInfo.remove(attribute, CodeAttribute.tag);
365    }
366
367    /**
368     * Adds a Code attribute.
369     *
370     * <p>
371     * The added attribute must share the same constant pool table as this
372     * <code>method_info</code> structure.
373     */
374    public void setCodeAttribute(CodeAttribute cattr) {
375        removeCodeAttribute();
376        if (attribute == null)
377            attribute = new ArrayList();
378
379        attribute.add(cattr);
380    }
381
382    /**
383     * Rebuilds a stack map table if the class file is for Java 6
384     * or later.  Java 5 or older Java VMs do not recognize a stack
385     * map table.  If <code>doPreverify</code> is true, this method
386     * also rebuilds a stack map for J2ME (CLDC).
387     *
388     * @param pool          used for making type hierarchy.
389     * @param cf            rebuild if this class file is for Java 6 or later.
390     * @see #rebuildStackMap(ClassPool)
391     * @see #rebuildStackMapForME(ClassPool)
392     * @since 3.6
393     */
394    public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
395        throws BadBytecode
396    {
397        if (cf.getMajorVersion() >= ClassFile.JAVA_6)
398            rebuildStackMap(pool);
399
400        if (doPreverify)
401            rebuildStackMapForME(pool);
402    }
403
404    /**
405     * Rebuilds a stack map table.  If no stack map table is included,
406     * a new one is created.  If this <code>MethodInfo</code> does not
407     * include a code attribute, nothing happens.
408     *
409     * @param pool          used for making type hierarchy.
410     * @see StackMapTable
411     * @since 3.6
412     */
413    public void rebuildStackMap(ClassPool pool) throws BadBytecode {
414        CodeAttribute ca = getCodeAttribute();
415        if (ca != null) {
416            StackMapTable smt = MapMaker.make(pool, this);
417            ca.setAttribute(smt);
418        }
419    }
420
421    /**
422     * Rebuilds a stack map table for J2ME (CLDC).  If no stack map table is included,
423     * a new one is created.  If this <code>MethodInfo</code> does not
424     * include a code attribute, nothing happens.
425     *
426     * @param pool          used for making type hierarchy.
427     * @see StackMapTable
428     * @since 3.12
429     */
430    public void rebuildStackMapForME(ClassPool pool) throws BadBytecode {
431        CodeAttribute ca = getCodeAttribute();
432        if (ca != null) {
433            StackMap sm = MapMaker.make2(pool, this);
434            ca.setAttribute(sm);
435        }
436    }
437
438    /**
439     * Returns the line number of the source line corresponding to the specified
440     * bytecode contained in this method.
441     *
442     * @param pos
443     *            the position of the bytecode (&gt;= 0). an index into the code
444     *            array.
445     * @return -1 if this information is not available.
446     */
447    public int getLineNumber(int pos) {
448        CodeAttribute ca = getCodeAttribute();
449        if (ca == null)
450            return -1;
451
452        LineNumberAttribute ainfo = (LineNumberAttribute)ca
453                .getAttribute(LineNumberAttribute.tag);
454        if (ainfo == null)
455            return -1;
456
457        return ainfo.toLineNumber(pos);
458    }
459
460    /**
461     * Changes a super constructor called by this constructor.
462     *
463     * <p>
464     * This method modifies a call to <code>super()</code>, which should be
465     * at the head of a constructor body, so that a constructor in a different
466     * super class is called. This method does not change actual parameters.
467     * Hence the new super class must have a constructor with the same signature
468     * as the original one.
469     *
470     * <p>
471     * This method should be called when the super class of the class declaring
472     * this method is changed.
473     *
474     * <p>
475     * This method does not perform anything unless this <code>MethodInfo</code>
476     * represents a constructor.
477     *
478     * @param superclass
479     *            the new super class
480     */
481    public void setSuperclass(String superclass) throws BadBytecode {
482        if (!isConstructor())
483            return;
484
485        CodeAttribute ca = getCodeAttribute();
486        byte[] code = ca.getCode();
487        CodeIterator iterator = ca.iterator();
488        int pos = iterator.skipSuperConstructor();
489        if (pos >= 0) { // not this()
490            ConstPool cp = constPool;
491            int mref = ByteArray.readU16bit(code, pos + 1);
492            int nt = cp.getMethodrefNameAndType(mref);
493            int sc = cp.addClassInfo(superclass);
494            int mref2 = cp.addMethodrefInfo(sc, nt);
495            ByteArray.write16bit(mref2, code, pos + 1);
496        }
497    }
498
499    private void read(MethodInfo src, String methodname, Map classnames)
500            throws BadBytecode {
501        ConstPool destCp = constPool;
502        accessFlags = src.accessFlags;
503        name = destCp.addUtf8Info(methodname);
504        cachedName = methodname;
505        ConstPool srcCp = src.constPool;
506        String desc = srcCp.getUtf8Info(src.descriptor);
507        String desc2 = Descriptor.rename(desc, classnames);
508        descriptor = destCp.addUtf8Info(desc2);
509
510        attribute = new ArrayList();
511        ExceptionsAttribute eattr = src.getExceptionsAttribute();
512        if (eattr != null)
513            attribute.add(eattr.copy(destCp, classnames));
514
515        CodeAttribute cattr = src.getCodeAttribute();
516        if (cattr != null)
517            attribute.add(cattr.copy(destCp, classnames));
518    }
519
520    private void read(DataInputStream in) throws IOException {
521        accessFlags = in.readUnsignedShort();
522        name = in.readUnsignedShort();
523        descriptor = in.readUnsignedShort();
524        int n = in.readUnsignedShort();
525        attribute = new ArrayList();
526        for (int i = 0; i < n; ++i)
527            attribute.add(AttributeInfo.read(constPool, in));
528    }
529
530    void write(DataOutputStream out) throws IOException {
531        out.writeShort(accessFlags);
532        out.writeShort(name);
533        out.writeShort(descriptor);
534
535        if (attribute == null)
536            out.writeShort(0);
537        else {
538            out.writeShort(attribute.size());
539            AttributeInfo.writeAll(attribute, out);
540        }
541    }
542}
543