001: /*
002: * Javassist, a Java-bytecode translator toolkit.
003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
004: *
005: * The contents of this file are subject to the Mozilla Public License Version
006: * 1.1 (the "License"); you may not use this file except in compliance with
007: * the License. Alternatively, the contents of this file may be used under
008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
009: *
010: * Software distributed under the License is distributed on an "AS IS" basis,
011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012: * for the specific language governing rights and limitations under the
013: * License.
014: */
015:
016: package javassist.bytecode;
017:
018: import java.io.DataInputStream;
019: import java.io.DataOutputStream;
020: import java.io.IOException;
021: import java.util.LinkedList;
022: import java.util.List;
023: import java.util.Map;
024:
025: /**
026: * <code>method_info</code> structure.
027: *
028: * @see javassist.CtMethod#getMethodInfo()
029: * @see javassist.CtConstructor#getMethodInfo()
030: */
031: public final class MethodInfo {
032: ConstPool constPool;
033: int accessFlags;
034: int name;
035: String cachedName;
036: int descriptor;
037: LinkedList attribute; // may be null
038:
039: /**
040: * The name of constructors: <code><init></code>.
041: */
042: public static final String nameInit = "<init>";
043:
044: /**
045: * The name of class initializer (static initializer):
046: * <code><clinit></code>.
047: */
048: public static final String nameClinit = "<clinit>";
049:
050: private MethodInfo(ConstPool cp) {
051: constPool = cp;
052: attribute = null;
053: }
054:
055: /**
056: * Constructs a <code>method_info</code> structure. The initial value of
057: * <code>access_flags</code> is zero.
058: *
059: * @param cp
060: * a constant pool table
061: * @param methodname
062: * method name
063: * @param desc
064: * method descriptor
065: * @see Descriptor
066: */
067: public MethodInfo(ConstPool cp, String methodname, String desc) {
068: this (cp);
069: accessFlags = 0;
070: name = cp.addUtf8Info(methodname);
071: cachedName = methodname;
072: descriptor = constPool.addUtf8Info(desc);
073: }
074:
075: MethodInfo(ConstPool cp, DataInputStream in) throws IOException {
076: this (cp);
077: read(in);
078: }
079:
080: /**
081: * Constructs a copy of <code>method_info</code> structure. Class names
082: * appearing in the source <code>method_info</code> are renamed according
083: * to <code>classnameMap</code>.
084: *
085: * <p>
086: * Note: only <code>Code</code> and <code>Exceptions</code> attributes
087: * are copied from the source. The other attributes are ignored.
088: *
089: * @param cp
090: * a constant pool table
091: * @param methodname
092: * a method name
093: * @param src
094: * a source <code>method_info</code>
095: * @param classnameMap
096: * specifies pairs of replaced and substituted name.
097: * @see Descriptor
098: */
099: public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
100: Map classnameMap) throws BadBytecode {
101: this (cp);
102: read(src, methodname, classnameMap);
103: }
104:
105: /**
106: * Returns a string representation of the object.
107: */
108: public String toString() {
109: return getName() + " " + getDescriptor();
110: }
111:
112: /**
113: * Copies all constant pool items to a given new constant pool
114: * and replaces the original items with the new ones.
115: * This is used for garbage collecting the items of removed fields
116: * and methods.
117: *
118: * @param cp the destination
119: */
120: void compact(ConstPool cp) {
121: name = cp.addUtf8Info(getName());
122: descriptor = cp.addUtf8Info(getDescriptor());
123: attribute = AttributeInfo.copyAll(attribute, cp);
124: constPool = cp;
125: }
126:
127: void prune(ConstPool cp) {
128: LinkedList newAttributes = new LinkedList();
129:
130: AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag);
131: if (invisibleAnnotations != null) {
132: invisibleAnnotations = invisibleAnnotations.copy(cp, null);
133: newAttributes.add(invisibleAnnotations);
134: }
135:
136: AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag);
137: if (visibleAnnotations != null) {
138: visibleAnnotations = visibleAnnotations.copy(cp, null);
139: newAttributes.add(visibleAnnotations);
140: }
141:
142: AttributeInfo parameterInvisibleAnnotations = getAttribute(ParameterAnnotationsAttribute.invisibleTag);
143: if (parameterInvisibleAnnotations != null) {
144: parameterInvisibleAnnotations = parameterInvisibleAnnotations
145: .copy(cp, null);
146: newAttributes.add(parameterInvisibleAnnotations);
147: }
148:
149: AttributeInfo parameterVisibleAnnotations = getAttribute(ParameterAnnotationsAttribute.visibleTag);
150: if (parameterVisibleAnnotations != null) {
151: parameterVisibleAnnotations = parameterVisibleAnnotations
152: .copy(cp, null);
153: newAttributes.add(parameterVisibleAnnotations);
154: }
155:
156: AnnotationDefaultAttribute defaultAttribute = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag);
157: if (defaultAttribute != null)
158: newAttributes.add(defaultAttribute);
159:
160: ExceptionsAttribute ea = getExceptionsAttribute();
161: if (ea != null)
162: newAttributes.add(ea);
163:
164: attribute = newAttributes;
165: name = cp.addUtf8Info(getName());
166: descriptor = cp.addUtf8Info(getDescriptor());
167: constPool = cp;
168: }
169:
170: /**
171: * Returns a method name.
172: */
173: public String getName() {
174: if (cachedName == null)
175: cachedName = constPool.getUtf8Info(name);
176:
177: return cachedName;
178: }
179:
180: /**
181: * Sets a method name.
182: */
183: public void setName(String newName) {
184: name = constPool.addUtf8Info(newName);
185: cachedName = newName;
186: }
187:
188: /**
189: * Returns true if this is not a constructor or a class initializer (static
190: * initializer).
191: */
192: public boolean isMethod() {
193: String n = getName();
194: return !n.equals(nameInit) && !n.equals(nameClinit);
195: }
196:
197: /**
198: * Returns a constant pool table used by this method.
199: */
200: public ConstPool getConstPool() {
201: return constPool;
202: }
203:
204: /**
205: * Returns true if this is a constructor.
206: */
207: public boolean isConstructor() {
208: return getName().equals(nameInit);
209: }
210:
211: /**
212: * Returns true if this is a class initializer (static initializer).
213: */
214: public boolean isStaticInitializer() {
215: return getName().equals(nameClinit);
216: }
217:
218: /**
219: * Returns access flags.
220: *
221: * @see AccessFlag
222: */
223: public int getAccessFlags() {
224: return accessFlags;
225: }
226:
227: /**
228: * Sets access flags.
229: *
230: * @see AccessFlag
231: */
232: public void setAccessFlags(int acc) {
233: accessFlags = acc;
234: }
235:
236: /**
237: * Returns a method descriptor.
238: *
239: * @see Descriptor
240: */
241: public String getDescriptor() {
242: return constPool.getUtf8Info(descriptor);
243: }
244:
245: /**
246: * Sets a method descriptor.
247: *
248: * @see Descriptor
249: */
250: public void setDescriptor(String desc) {
251: if (!desc.equals(getDescriptor()))
252: descriptor = constPool.addUtf8Info(desc);
253: }
254:
255: /**
256: * Returns all the attributes. The returned <code>List</code> object
257: * is shared with this object. If you add a new attribute to the list,
258: * the attribute is also added to the method represented by this
259: * object. If you remove an attribute from the list, it is also removed
260: * from the method.
261: *
262: * @return a list of <code>AttributeInfo</code> objects.
263: * @see AttributeInfo
264: */
265: public List getAttributes() {
266: if (attribute == null)
267: attribute = new LinkedList();
268:
269: return attribute;
270: }
271:
272: /**
273: * Returns the attribute with the specified name. If it is not found, this
274: * method returns null.
275: *
276: * @param name attribute name
277: * @return an <code>AttributeInfo</code> object or null.
278: * @see #getAttributes()
279: */
280: public AttributeInfo getAttribute(String name) {
281: return AttributeInfo.lookup(attribute, name);
282: }
283:
284: /**
285: * Appends an attribute. If there is already an attribute with the same
286: * name, the new one substitutes for it.
287: *
288: * @see #getAttributes()
289: */
290: public void addAttribute(AttributeInfo info) {
291: if (attribute == null)
292: attribute = new LinkedList();
293:
294: AttributeInfo.remove(attribute, info.getName());
295: attribute.add(info);
296: }
297:
298: /**
299: * Returns an Exceptions attribute.
300: *
301: * @return an Exceptions attribute or null if it is not specified.
302: */
303: public ExceptionsAttribute getExceptionsAttribute() {
304: AttributeInfo info = AttributeInfo.lookup(attribute,
305: ExceptionsAttribute.tag);
306: return (ExceptionsAttribute) info;
307: }
308:
309: /**
310: * Returns a Code attribute.
311: *
312: * @return a Code attribute or null if it is not specified.
313: */
314: public CodeAttribute getCodeAttribute() {
315: AttributeInfo info = AttributeInfo.lookup(attribute,
316: CodeAttribute.tag);
317: return (CodeAttribute) info;
318: }
319:
320: /**
321: * Removes an Exception attribute.
322: */
323: public void removeExceptionsAttribute() {
324: AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
325: }
326:
327: /**
328: * Adds an Exception attribute.
329: *
330: * <p>
331: * The added attribute must share the same constant pool table as this
332: * <code>method_info</code> structure.
333: */
334: public void setExceptionsAttribute(ExceptionsAttribute cattr) {
335: removeExceptionsAttribute();
336: if (attribute == null)
337: attribute = new LinkedList();
338:
339: attribute.add(cattr);
340: }
341:
342: /**
343: * Removes a Code attribute.
344: */
345: public void removeCodeAttribute() {
346: AttributeInfo.remove(attribute, CodeAttribute.tag);
347: }
348:
349: /**
350: * Adds a Code attribute.
351: *
352: * <p>
353: * The added attribute must share the same constant pool table as this
354: * <code>method_info</code> structure.
355: */
356: public void setCodeAttribute(CodeAttribute cattr) {
357: removeCodeAttribute();
358: if (attribute == null)
359: attribute = new LinkedList();
360:
361: attribute.add(cattr);
362: }
363:
364: /**
365: * Returns the line number of the source line corresponding to the specified
366: * bytecode contained in this method.
367: *
368: * @param pos
369: * the position of the bytecode (>= 0). an index into the code
370: * array.
371: * @return -1 if this information is not available.
372: */
373: public int getLineNumber(int pos) {
374: CodeAttribute ca = getCodeAttribute();
375: if (ca == null)
376: return -1;
377:
378: LineNumberAttribute ainfo = (LineNumberAttribute) ca
379: .getAttribute(LineNumberAttribute.tag);
380: if (ainfo == null)
381: return -1;
382:
383: return ainfo.toLineNumber(pos);
384: }
385:
386: /**
387: * Changes a super constructor called by this constructor.
388: *
389: * <p>
390: * This method modifies a call to <code>super()</code>, which should be
391: * at the head of a constructor body, so that a constructor in a different
392: * super class is called. This method does not change actural parameters.
393: * Hence the new super class must have a constructor with the same signature
394: * as the original one.
395: *
396: * <p>
397: * This method should be called when the super class of the class declaring
398: * this method is changed.
399: *
400: * <p>
401: * This method does not perform anything unless this <code>MethodInfo</code>
402: * represents a constructor.
403: *
404: * @param superclass
405: * the new super class
406: */
407: public void setSuperclass(String super class) throws BadBytecode {
408: if (!isConstructor())
409: return;
410:
411: CodeAttribute ca = getCodeAttribute();
412: byte[] code = ca.getCode();
413: CodeIterator iterator = ca.iterator();
414: int pos = iterator.skipSuperConstructor();
415: if (pos >= 0) { // not this()
416: ConstPool cp = constPool;
417: int mref = ByteArray.readU16bit(code, pos + 1);
418: int nt = cp.getMethodrefNameAndType(mref);
419: int sc = cp.addClassInfo(super class);
420: int mref2 = cp.addMethodrefInfo(sc, nt);
421: ByteArray.write16bit(mref2, code, pos + 1);
422: }
423: }
424:
425: private void read(MethodInfo src, String methodname, Map classnames)
426: throws BadBytecode {
427: ConstPool destCp = constPool;
428: accessFlags = src.accessFlags;
429: name = destCp.addUtf8Info(methodname);
430: cachedName = methodname;
431: ConstPool srcCp = src.constPool;
432: String desc = srcCp.getUtf8Info(src.descriptor);
433: String desc2 = Descriptor.rename(desc, classnames);
434: descriptor = destCp.addUtf8Info(desc2);
435:
436: attribute = new LinkedList();
437: ExceptionsAttribute eattr = src.getExceptionsAttribute();
438: if (eattr != null)
439: attribute.add(eattr.copy(destCp, classnames));
440:
441: CodeAttribute cattr = src.getCodeAttribute();
442: if (cattr != null)
443: attribute.add(cattr.copy(destCp, classnames));
444: }
445:
446: private void read(DataInputStream in) throws IOException {
447: accessFlags = in.readUnsignedShort();
448: name = in.readUnsignedShort();
449: descriptor = in.readUnsignedShort();
450: int n = in.readUnsignedShort();
451: attribute = new LinkedList();
452: for (int i = 0; i < n; ++i)
453: attribute.add(AttributeInfo.read(constPool, in));
454: }
455:
456: void write(DataOutputStream out) throws IOException {
457: out.writeShort(accessFlags);
458: out.writeShort(name);
459: out.writeShort(descriptor);
460:
461: if (attribute == null)
462: out.writeShort(0);
463: else {
464: out.writeShort(attribute.size());
465: AttributeInfo.writeAll(attribute, out);
466: }
467: }
468: }
|