/*
* Copyright 2004-2010 Brian S O'Neill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cojen.classfile;
import java.util.ArrayList;
import java.util.List;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Modifier;
import org.cojen.classfile.attribute.Annotation;
import org.cojen.classfile.attribute.AnnotationsAttr;
import org.cojen.classfile.attribute.CodeAttr;
import org.cojen.classfile.attribute.DeprecatedAttr;
import org.cojen.classfile.attribute.EnclosingMethodAttr;
import org.cojen.classfile.attribute.ExceptionsAttr;
import org.cojen.classfile.attribute.RuntimeInvisibleAnnotationsAttr;
import org.cojen.classfile.attribute.RuntimeVisibleAnnotationsAttr;
import org.cojen.classfile.attribute.SignatureAttr;
import org.cojen.classfile.attribute.SourceFileAttr;
import org.cojen.classfile.attribute.SyntheticAttr;
import org.cojen.classfile.constant.ConstantClassInfo;
import org.cojen.classfile.constant.ConstantUTFInfo;
/**
* This class corresponds to the method_info data structure as defined in
* section 4.6 of <i>The Java Virtual Machine Specification</i>.
* To make it easier to create bytecode for a method's CodeAttr, the
* CodeBuilder class is provided.
*
* @author Brian S O'Neill
* @see ClassFile
* @see CodeBuilder
*/
public class MethodInfo {
private ClassFile mParent;
private ConstantPool mCp;
private String mName;
private MethodDesc mDesc;
private Modifiers mModifiers;
private ConstantUTFInfo mNameConstant;
private ConstantUTFInfo mDescriptorConstant;
private List<Attribute> mAttributes = new ArrayList<Attribute>(2);
private CodeAttr mCode;
private ExceptionsAttr mExceptions;
private int mAnonymousInnerClassCount = 0;
MethodInfo(ClassFile parent,
Modifiers modifiers,
String name,
MethodDesc desc) {
mParent = parent;
mCp = parent.getConstantPool();
mName = name;
mDesc = desc;
mModifiers = modifiers;
mNameConstant = mCp.addConstantUTF(name);
mDescriptorConstant = mCp.addConstantUTF(desc.getDescriptor());
if (!modifiers.isAbstract() && !modifiers.isNative()) {
addAttribute(new CodeAttr(mCp));
}
}
private MethodInfo(ClassFile parent,
int modifier,
ConstantUTFInfo nameConstant,
ConstantUTFInfo descConstant) {
mParent = parent;
mCp = parent.getConstantPool();
mName = nameConstant.getValue();
mDesc = MethodDesc.forDescriptor(descConstant.getValue());
mModifiers = Modifiers.getInstance(modifier);
mNameConstant = nameConstant;
mDescriptorConstant = descConstant;
}
/**
* Returns the parent ClassFile for this MethodInfo.
*/
public ClassFile getClassFile() {
return mParent;
}
/**
* Returns the name of this method.
*/
public String getName() {
return mName;
}
/**
* Returns a MethodDesc which describes return and parameter types
* of this method.
*/
public MethodDesc getMethodDescriptor() {
return mDesc;
}
/**
* Returns this method's modifiers.
*/
public Modifiers getModifiers() {
return mModifiers;
}
public void setModifiers(Modifiers modifiers) {
mModifiers = modifiers;
}
/**
* Returns a constant from the constant pool with this method's name.
*/
public ConstantUTFInfo getNameConstant() {
return mNameConstant;
}
/**
* Returns a constant from the constant pool with this method's type
* descriptor string.
* @see MethodDesc
*/
public ConstantUTFInfo getDescriptorConstant() {
return mDescriptorConstant;
}
/**
* Returns the exceptions that this method is declared to throw.
*/
public TypeDesc[] getExceptions() {
if (mExceptions == null) {
return new TypeDesc[0];
}
ConstantClassInfo[] classes = mExceptions.getExceptions();
TypeDesc[] types = new TypeDesc[classes.length];
for (int i=0; i<types.length; i++) {
types[i] = classes[i].getType();
}
return types;
}
/**
* Returns a CodeAttr object used to manipulate the method code body, or
* null if this method is abstract or native.
*/
public CodeAttr getCodeAttr() {
return mCode;
}
public boolean isSynthetic() {
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute attr = mAttributes.get(i);
if (attr instanceof SyntheticAttr) {
return true;
}
}
return false;
}
public boolean isDeprecated() {
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute attr = mAttributes.get(i);
if (attr instanceof DeprecatedAttr) {
return true;
}
}
return false;
}
/**
* Returns all the runtime invisible annotations defined for this class
* file, or an empty array if none.
*/
public Annotation[] getRuntimeInvisibleAnnotations() {
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute attr = mAttributes.get(i);
if (attr instanceof RuntimeInvisibleAnnotationsAttr) {
return ((AnnotationsAttr) attr).getAnnotations();
}
}
return new Annotation[0];
}
/**
* Returns all the runtime visible annotations defined for this class file,
* or an empty array if none.
*/
public Annotation[] getRuntimeVisibleAnnotations() {
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute attr = mAttributes.get(i);
if (attr instanceof RuntimeVisibleAnnotationsAttr) {
return ((AnnotationsAttr) attr).getAnnotations();
}
}
return new Annotation[0];
}
/**
* Add a runtime invisible annotation.
*/
public Annotation addRuntimeInvisibleAnnotation(TypeDesc type) {
AnnotationsAttr attr = null;
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute a = mAttributes.get(i);
if (a instanceof RuntimeInvisibleAnnotationsAttr) {
attr = (AnnotationsAttr) a;
}
}
if (attr == null) {
attr = new RuntimeInvisibleAnnotationsAttr(mCp);
addAttribute(attr);
}
Annotation ann = new Annotation(mCp);
ann.setType(type);
attr.addAnnotation(ann);
return ann;
}
/**
* Add a runtime visible annotation.
*/
public Annotation addRuntimeVisibleAnnotation(TypeDesc type) {
AnnotationsAttr attr = null;
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute a = mAttributes.get(i);
if (a instanceof RuntimeVisibleAnnotationsAttr) {
attr = (AnnotationsAttr) a;
}
}
if (attr == null) {
attr = new RuntimeVisibleAnnotationsAttr(mCp);
addAttribute(attr);
}
Annotation ann = new Annotation(mCp);
ann.setType(type);
attr.addAnnotation(ann);
return ann;
}
/**
* Returns the signature attribute of this method, or null if none is
* defined.
*/
// TODO: Eventually remove this method
public SignatureAttr getSignatureAttr() {
for (int i = mAttributes.size(); --i >= 0; ) {
Attribute attr = mAttributes.get(i);
if (attr instanceof SignatureAttr) {
return (SignatureAttr) attr;
}
}
return null;
}
/**
* Add a declared exception that this method may throw.
*/
public void addException(TypeDesc type) {
if (mExceptions == null) {
addAttribute(new ExceptionsAttr(mCp));
}
// TODO: Special handling for generics
ConstantClassInfo cci = mCp.addConstantClass(type);
mExceptions.addException(cci);
}
/**
* Add an inner class to this method.
*
* @param innerClassName Optional short inner class name.
*/
public ClassFile addInnerClass(String innerClassName) {
return addInnerClass(innerClassName, (String)null);
}
/**
* Add an inner class to this method.
*
* @param innerClassName Optional short inner class name.
* @param superClass Super class.
*/
public ClassFile addInnerClass(String innerClassName, Class superClass) {
return addInnerClass(innerClassName, superClass.getName());
}
/**
* Add an inner class to this method.
*
* @param innerClassName Optional short inner class name.
* @param superClassName Full super class name.
*/
public ClassFile addInnerClass(String innerClassName, String superClassName) {
ClassFile inner;
if (innerClassName == null) {
inner = mParent.addInnerClass(null, null, superClassName);
} else {
String fullInnerClassName = mParent.getClassName() + '$' +
(++mAnonymousInnerClassCount) + innerClassName;
inner = mParent.addInnerClass(fullInnerClassName, innerClassName, superClassName);
}
if (mParent.getMajorVersion() >= 49) {
inner.addAttribute(new EnclosingMethodAttr
(mCp, mCp.addConstantClass(mParent.getClassName()),
mCp.addConstantNameAndType(mNameConstant, mDescriptorConstant)));
}
return inner;
}
/**
* Mark this method as being synthetic by adding a special attribute.
*/
public void markSynthetic() {
addAttribute(new SyntheticAttr(mCp));
}
/**
* Mark this method as being deprecated by adding a special attribute.
*/
public void markDeprecated() {
addAttribute(new DeprecatedAttr(mCp));
}
public void addAttribute(Attribute attr) {
if (attr instanceof CodeAttr) {
if (mCode != null) {
mAttributes.remove(mCode);
}
mCode = (CodeAttr)attr;
mCode.initialStackMapFrame(this);
} else if (attr instanceof ExceptionsAttr) {
if (mExceptions != null) {
mAttributes.remove(mExceptions);
}
mExceptions = (ExceptionsAttr)attr;
}
mAttributes.add(attr);
}
public Attribute[] getAttributes() {
return mAttributes.toArray(new Attribute[mAttributes.size()]);
}
/**
* Returns the length (in bytes) of this object in the class file.
*/
public int getLength() {
int length = 8;
int size = mAttributes.size();
for (int i=0; i<size; i++) {
length += mAttributes.get(i).getLength();
}
return length;
}
public void writeTo(DataOutput dout) throws IOException {
dout.writeShort(mModifiers.getBitmask());
dout.writeShort(mNameConstant.getIndex());
dout.writeShort(mDescriptorConstant.getIndex());
int size = mAttributes.size();
dout.writeShort(size);
for (int i=0; i<size; i++) {
Attribute attr = mAttributes.get(i);
try {
attr.writeTo(dout);
} catch (IllegalStateException e) {
IllegalStateException e2 =
new IllegalStateException(e.getMessage() + ": " + toString());
try {
e2.initCause(e);
} catch (NoSuchMethodError e3) {
}
throw e2;
}
}
}
public String toString() {
String str = mDesc.toMethodSignature(getName(), mModifiers.isVarArgs());
String modStr = mModifiers.toString();
if (modStr.length() > 0) {
str = modStr + ' ' + str;
}
return str;
}
static MethodInfo readFrom(ClassFile parent,
DataInput din,
AttributeFactory attrFactory)
throws IOException
{
ConstantPool cp = parent.getConstantPool();
int modifier = din.readUnsignedShort();
int index = din.readUnsignedShort();
ConstantUTFInfo nameConstant = (ConstantUTFInfo)cp.getConstant(index);
index = din.readUnsignedShort();
ConstantUTFInfo descConstant = (ConstantUTFInfo)cp.getConstant(index);
MethodInfo info = new MethodInfo(parent, modifier,
nameConstant, descConstant);
// Read attributes.
int size = din.readUnsignedShort();
for (int i=0; i<size; i++) {
info.addAttribute(Attribute.readFrom(cp, din, attrFactory));
}
return info;
}
}