1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.bytecode;
17 
18 import java.io.DataInputStream;
19 import java.io.DataOutputStream;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Map;
24 import javassist.ClassPool;
25 import javassist.bytecode.stackmap.MapMaker;
26 
27 /**
28  * <code>method_info</code> structure.
29  *
30  * @see javassist.CtMethod#getMethodInfo()
31  * @see javassist.CtConstructor#getMethodInfo()
32  */
33 public 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 
MethodInfo(ConstPool cp)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      */
MethodInfo(ConstPool cp, String methodname, String desc)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 
MethodInfo(ConstPool cp, DataInputStream in)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      */
MethodInfo(ConstPool cp, String methodname, MethodInfo src, Map classnameMap)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      */
toString()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      */
compact(ConstPool cp)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 
prune(ConstPool cp)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      */
getName()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      */
setName(String newName)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      */
isMethod()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      */
getConstPool()219     public ConstPool getConstPool() {
220         return constPool;
221     }
222 
223     /**
224      * Returns true if this is a constructor.
225      */
isConstructor()226     public boolean isConstructor() {
227         return getName().equals(nameInit);
228     }
229 
230     /**
231      * Returns true if this is a class initializer (static initializer).
232      */
isStaticInitializer()233     public boolean isStaticInitializer() {
234         return getName().equals(nameClinit);
235     }
236 
237     /**
238      * Returns access flags.
239      *
240      * @see AccessFlag
241      */
getAccessFlags()242     public int getAccessFlags() {
243         return accessFlags;
244     }
245 
246     /**
247      * Sets access flags.
248      *
249      * @see AccessFlag
250      */
setAccessFlags(int acc)251     public void setAccessFlags(int acc) {
252         accessFlags = acc;
253     }
254 
255     /**
256      * Returns a method descriptor.
257      *
258      * @see Descriptor
259      */
getDescriptor()260     public String getDescriptor() {
261         return constPool.getUtf8Info(descriptor);
262     }
263 
264     /**
265      * Sets a method descriptor.
266      *
267      * @see Descriptor
268      */
setDescriptor(String desc)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      */
getAttributes()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      */
getAttribute(String name)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      */
addAttribute(AttributeInfo info)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      */
getExceptionsAttribute()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      */
getCodeAttribute()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      */
removeExceptionsAttribute()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      */
setExceptionsAttribute(ExceptionsAttribute cattr)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      */
removeCodeAttribute()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      */
setCodeAttribute(CodeAttribute cattr)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      */
rebuildStackMapIf6(ClassPool pool, ClassFile cf)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      */
rebuildStackMap(ClassPool pool)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      */
rebuildStackMapForME(ClassPool pool)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      */
getLineNumber(int pos)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      */
setSuperclass(String superclass)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 
read(MethodInfo src, String methodname, Map classnames)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 
read(DataInputStream in)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 
write(DataOutputStream out)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