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