001: /*
002: * Copyright 2004 Brian S O'Neill
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cojen.classfile;
018:
019: import java.util.ArrayList;
020: import java.util.List;
021: import java.io.DataInput;
022: import java.io.DataOutput;
023: import java.io.IOException;
024: import java.lang.reflect.Modifier;
025: import org.cojen.classfile.attribute.Annotation;
026: import org.cojen.classfile.attribute.AnnotationsAttr;
027: import org.cojen.classfile.attribute.CodeAttr;
028: import org.cojen.classfile.attribute.DeprecatedAttr;
029: import org.cojen.classfile.attribute.EnclosingMethodAttr;
030: import org.cojen.classfile.attribute.ExceptionsAttr;
031: import org.cojen.classfile.attribute.RuntimeInvisibleAnnotationsAttr;
032: import org.cojen.classfile.attribute.RuntimeVisibleAnnotationsAttr;
033: import org.cojen.classfile.attribute.SignatureAttr;
034: import org.cojen.classfile.attribute.SourceFileAttr;
035: import org.cojen.classfile.attribute.SyntheticAttr;
036: import org.cojen.classfile.constant.ConstantClassInfo;
037: import org.cojen.classfile.constant.ConstantUTFInfo;
038:
039: /**
040: * This class corresponds to the method_info data structure as defined in
041: * section 4.6 of <i>The Java Virtual Machine Specification</i>.
042: * To make it easier to create bytecode for a method's CodeAttr, the
043: * CodeBuilder class is provided.
044: *
045: * @author Brian S O'Neill
046: * @see ClassFile
047: * @see CodeBuilder
048: */
049: public class MethodInfo {
050: private ClassFile mParent;
051: private ConstantPool mCp;
052:
053: private String mName;
054: private MethodDesc mDesc;
055:
056: private Modifiers mModifiers;
057:
058: private ConstantUTFInfo mNameConstant;
059: private ConstantUTFInfo mDescriptorConstant;
060:
061: private List mAttributes = new ArrayList(2);
062:
063: private CodeAttr mCode;
064: private ExceptionsAttr mExceptions;
065:
066: private int mAnonymousInnerClassCount = 0;
067:
068: MethodInfo(ClassFile parent, Modifiers modifiers, String name,
069: MethodDesc desc) {
070:
071: mParent = parent;
072: mCp = parent.getConstantPool();
073: mName = name;
074: mDesc = desc;
075:
076: mModifiers = modifiers;
077: mNameConstant = mCp.addConstantUTF(name);
078: mDescriptorConstant = mCp.addConstantUTF(desc.getDescriptor());
079:
080: if (!modifiers.isAbstract() && !modifiers.isNative()) {
081: addAttribute(new CodeAttr(mCp));
082: }
083: }
084:
085: private MethodInfo(ClassFile parent, int modifier,
086: ConstantUTFInfo nameConstant, ConstantUTFInfo descConstant) {
087:
088: mParent = parent;
089: mCp = parent.getConstantPool();
090: mName = nameConstant.getValue();
091: mDesc = MethodDesc.forDescriptor(descConstant.getValue());
092:
093: mModifiers = Modifiers.getInstance(modifier);
094: mNameConstant = nameConstant;
095: mDescriptorConstant = descConstant;
096: }
097:
098: /**
099: * Returns the parent ClassFile for this MethodInfo.
100: */
101: public ClassFile getClassFile() {
102: return mParent;
103: }
104:
105: /**
106: * Returns the name of this method.
107: */
108: public String getName() {
109: return mName;
110: }
111:
112: /**
113: * Returns a MethodDesc which describes return and parameter types
114: * of this method.
115: */
116: public MethodDesc getMethodDescriptor() {
117: return mDesc;
118: }
119:
120: /**
121: * Returns this method's modifiers.
122: */
123: public Modifiers getModifiers() {
124: return mModifiers;
125: }
126:
127: public void setModifiers(Modifiers modifiers) {
128: mModifiers = modifiers;
129: }
130:
131: /**
132: * Returns a constant from the constant pool with this method's name.
133: */
134: public ConstantUTFInfo getNameConstant() {
135: return mNameConstant;
136: }
137:
138: /**
139: * Returns a constant from the constant pool with this method's type
140: * descriptor string.
141: * @see MethodDesc
142: */
143: public ConstantUTFInfo getDescriptorConstant() {
144: return mDescriptorConstant;
145: }
146:
147: /**
148: * Returns the exceptions that this method is declared to throw.
149: */
150: public TypeDesc[] getExceptions() {
151: if (mExceptions == null) {
152: return new TypeDesc[0];
153: }
154:
155: ConstantClassInfo[] classes = mExceptions.getExceptions();
156: TypeDesc[] types = new TypeDesc[classes.length];
157: for (int i = 0; i < types.length; i++) {
158: types[i] = classes[i].getType();
159: }
160:
161: return types;
162: }
163:
164: /**
165: * Returns a CodeAttr object used to manipulate the method code body, or
166: * null if this method is abstract or native.
167: */
168: public CodeAttr getCodeAttr() {
169: return mCode;
170: }
171:
172: public boolean isSynthetic() {
173: for (int i = mAttributes.size(); --i >= 0;) {
174: Object obj = mAttributes.get(i);
175: if (obj instanceof SyntheticAttr) {
176: return true;
177: }
178: }
179: return false;
180: }
181:
182: public boolean isDeprecated() {
183: for (int i = mAttributes.size(); --i >= 0;) {
184: Object obj = mAttributes.get(i);
185: if (obj instanceof DeprecatedAttr) {
186: return true;
187: }
188: }
189: return false;
190: }
191:
192: /**
193: * Returns all the runtime invisible annotations defined for this class
194: * file, or an empty array if none.
195: */
196: public Annotation[] getRuntimeInvisibleAnnotations() {
197: for (int i = mAttributes.size(); --i >= 0;) {
198: Object obj = mAttributes.get(i);
199: if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
200: return ((AnnotationsAttr) obj).getAnnotations();
201: }
202: }
203: return new Annotation[0];
204: }
205:
206: /**
207: * Returns all the runtime visible annotations defined for this class file,
208: * or an empty array if none.
209: */
210: public Annotation[] getRuntimeVisibleAnnotations() {
211: for (int i = mAttributes.size(); --i >= 0;) {
212: Object obj = mAttributes.get(i);
213: if (obj instanceof RuntimeVisibleAnnotationsAttr) {
214: return ((AnnotationsAttr) obj).getAnnotations();
215: }
216: }
217: return new Annotation[0];
218: }
219:
220: /**
221: * Add a runtime invisible annotation.
222: */
223: public Annotation addRuntimeInvisibleAnnotation(TypeDesc type) {
224: AnnotationsAttr attr = null;
225: for (int i = mAttributes.size(); --i >= 0;) {
226: Object obj = mAttributes.get(i);
227: if (obj instanceof RuntimeInvisibleAnnotationsAttr) {
228: attr = (AnnotationsAttr) obj;
229: }
230: }
231: if (attr == null) {
232: attr = new RuntimeInvisibleAnnotationsAttr(mCp);
233: addAttribute(attr);
234: }
235: Annotation ann = new Annotation(mCp);
236: ann.setType(type);
237: attr.addAnnotation(ann);
238: return ann;
239: }
240:
241: /**
242: * Add a runtime visible annotation.
243: */
244: public Annotation addRuntimeVisibleAnnotation(TypeDesc type) {
245: AnnotationsAttr attr = null;
246: for (int i = mAttributes.size(); --i >= 0;) {
247: Object obj = mAttributes.get(i);
248: if (obj instanceof RuntimeVisibleAnnotationsAttr) {
249: attr = (AnnotationsAttr) obj;
250: }
251: }
252: if (attr == null) {
253: attr = new RuntimeVisibleAnnotationsAttr(mCp);
254: addAttribute(attr);
255: }
256: Annotation ann = new Annotation(mCp);
257: ann.setType(type);
258: attr.addAnnotation(ann);
259: return ann;
260: }
261:
262: /**
263: * Returns the signature attribute of this method, or null if none is
264: * defined.
265: */
266: // TODO: Eventually remove this method
267: public SignatureAttr getSignatureAttr() {
268: for (int i = mAttributes.size(); --i >= 0;) {
269: Object obj = mAttributes.get(i);
270: if (obj instanceof SignatureAttr) {
271: return (SignatureAttr) obj;
272: }
273: }
274: return null;
275: }
276:
277: /**
278: * Add a declared exception that this method may throw.
279: */
280: public void addException(TypeDesc type) {
281: if (mExceptions == null) {
282: addAttribute(new ExceptionsAttr(mCp));
283: }
284: // TODO: Special handling for generics
285: ConstantClassInfo cci = mCp.addConstantClass(type);
286: mExceptions.addException(cci);
287: }
288:
289: /**
290: * Add an inner class to this method.
291: *
292: * @param innerClassName Optional short inner class name.
293: */
294: public ClassFile addInnerClass(String innerClassName) {
295: return addInnerClass(innerClassName, (String) null);
296: }
297:
298: /**
299: * Add an inner class to this method.
300: *
301: * @param innerClassName Optional short inner class name.
302: * @param superClass Super class.
303: */
304: public ClassFile addInnerClass(String innerClassName,
305: Class super Class) {
306: return addInnerClass(innerClassName, super Class.getName());
307: }
308:
309: /**
310: * Add an inner class to this method.
311: *
312: * @param innerClassName Optional short inner class name.
313: * @param superClassName Full super class name.
314: */
315: public ClassFile addInnerClass(String innerClassName,
316: String super ClassName) {
317: ClassFile inner;
318: if (innerClassName == null) {
319: inner = mParent.addInnerClass(null, null, super ClassName);
320: } else {
321: String fullInnerClassName = mParent.getClassName() + '$'
322: + (++mAnonymousInnerClassCount) + innerClassName;
323: inner = mParent.addInnerClass(fullInnerClassName,
324: innerClassName, super ClassName);
325: }
326:
327: if (mParent.getMajorVersion() >= 49) {
328: inner.addAttribute(new EnclosingMethodAttr(mCp, mCp
329: .addConstantClass(mParent.getClassName()), mCp
330: .addConstantNameAndType(mNameConstant,
331: mDescriptorConstant)));
332: }
333:
334: return inner;
335: }
336:
337: /**
338: * Mark this method as being synthetic by adding a special attribute.
339: */
340: public void markSynthetic() {
341: addAttribute(new SyntheticAttr(mCp));
342: }
343:
344: /**
345: * Mark this method as being deprecated by adding a special attribute.
346: */
347: public void markDeprecated() {
348: addAttribute(new DeprecatedAttr(mCp));
349: }
350:
351: public void addAttribute(Attribute attr) {
352: if (attr instanceof CodeAttr) {
353: if (mCode != null) {
354: mAttributes.remove(mCode);
355: }
356: mCode = (CodeAttr) attr;
357: } else if (attr instanceof ExceptionsAttr) {
358: if (mExceptions != null) {
359: mAttributes.remove(mExceptions);
360: }
361: mExceptions = (ExceptionsAttr) attr;
362: }
363:
364: mAttributes.add(attr);
365: }
366:
367: public Attribute[] getAttributes() {
368: Attribute[] attrs = new Attribute[mAttributes.size()];
369: return (Attribute[]) mAttributes.toArray(attrs);
370: }
371:
372: /**
373: * Returns the length (in bytes) of this object in the class file.
374: */
375: public int getLength() {
376: int length = 8;
377:
378: int size = mAttributes.size();
379: for (int i = 0; i < size; i++) {
380: length += ((Attribute) mAttributes.get(i)).getLength();
381: }
382:
383: return length;
384: }
385:
386: public void writeTo(DataOutput dout) throws IOException {
387: dout.writeShort(mModifiers.getBitmask());
388: dout.writeShort(mNameConstant.getIndex());
389: dout.writeShort(mDescriptorConstant.getIndex());
390:
391: int size = mAttributes.size();
392: dout.writeShort(size);
393: for (int i = 0; i < size; i++) {
394: Attribute attr = (Attribute) mAttributes.get(i);
395: try {
396: attr.writeTo(dout);
397: } catch (IllegalStateException e) {
398: IllegalStateException e2 = new IllegalStateException(e
399: .getMessage()
400: + ": " + toString());
401: try {
402: e2.initCause(e);
403: } catch (NoSuchMethodError e3) {
404: }
405: throw e2;
406: }
407: }
408: }
409:
410: public String toString() {
411: String str = mDesc.toMethodSignature(getName(), mModifiers
412: .isVarArgs());
413: String modStr = mModifiers.toString();
414: if (modStr.length() > 0) {
415: str = modStr + ' ' + str;
416: }
417: return str;
418: }
419:
420: static MethodInfo readFrom(ClassFile parent, DataInput din,
421: AttributeFactory attrFactory) throws IOException {
422: ConstantPool cp = parent.getConstantPool();
423:
424: int modifier = din.readUnsignedShort();
425: int index = din.readUnsignedShort();
426: ConstantUTFInfo nameConstant = (ConstantUTFInfo) cp
427: .getConstant(index);
428: index = din.readUnsignedShort();
429: ConstantUTFInfo descConstant = (ConstantUTFInfo) cp
430: .getConstant(index);
431:
432: MethodInfo info = new MethodInfo(parent, modifier,
433: nameConstant, descConstant);
434:
435: // Read attributes.
436: int size = din.readUnsignedShort();
437: for (int i = 0; i < size; i++) {
438: info.addAttribute(Attribute.readFrom(cp, din, attrFactory));
439: }
440:
441: return info;
442: }
443: }
|