xapi.bytecode.MethodInfo Maven / Gradle / Ivy
Show all versions of xapi-dev Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* MODIFIED BY James Nelson of We The Internet, 2013.
* Repackaged to avoid conflicts with different versions of Javassist,
* and modified Javassist APIs to make them more accessible to outside code.
*/
package xapi.bytecode;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import xapi.bytecode.annotation.AnnotationDefaultAttribute;
import xapi.bytecode.annotation.AnnotationsAttribute;
import xapi.bytecode.attributes.AttributeInfo;
import xapi.bytecode.attributes.CodeAttribute;
import xapi.bytecode.attributes.ExceptionsAttribute;
import xapi.bytecode.attributes.LineNumberAttribute;
import xapi.bytecode.attributes.ParameterAnnotationsAttribute;
import xapi.bytecode.attributes.SignatureAttribute;
import xapi.util.X_Byte;
public class MethodInfo extends MemberInfo {
ConstPool constPool;
int accessFlags;
int name;
String cachedName;
int descriptor;
/**
* If this value is true, Javassist maintains a StackMap
attribute
* generated by the preverify
tool of J2ME (CLDC). The initial
* value of this field is false
.
*/
public static boolean doPreverify = false;
/**
* The name of constructors: <init>
.
*/
public static final String nameInit = "";
/**
* The name of class initializer (static initializer):
* <clinit>
.
*/
public static final String nameClinit = "";
private MethodInfo(final ConstPool cp) {
constPool = cp;
attribute = null;
}
/**
* Constructs a method_info
structure. The initial value of
* access_flags
is zero.
*
* @param cp
* a constant pool table
* @param methodname
* method name
* @param desc
* method descriptor
* @see Descriptor
*/
public MethodInfo(final ConstPool cp, final String methodname, final String desc) {
this(cp);
accessFlags = 0;
name = cp.addUtf8Info(methodname);
cachedName = methodname;
descriptor = constPool.addUtf8Info(desc);
}
MethodInfo(final ConstPool cp, final DataInput in) throws IOException {
this(cp);
read(in);
}
/**
* Constructs a copy of method_info
structure. Class names
* appearing in the source method_info
are renamed according
* to classnameMap
.
*
*
* Note: only Code
and Exceptions
attributes
* are copied from the source. The other attributes are ignored.
*
* @param cp
* a constant pool table
* @param methodname
* a method name
* @param src
* a source method_info
* @param classnameMap
* specifies pairs of replaced and substituted name.
* @see Descriptor
*/
public MethodInfo(final ConstPool cp, final String methodname, final MethodInfo src,
final Map classnameMap) throws BadBytecode {
this(cp);
read(src, methodname, classnameMap);
}
/**
* Returns a string representation of the object.
*/
@Override
public String toString() {
return getSignature();
}
@Override
public String getSignature() {
return getName() + " " + getDescriptor();
}
/**
* Copies all constant pool items to a given new constant pool
* and replaces the original items with the new ones.
* This is used for garbage collecting the items of removed fields
* and methods.
*
* @param cp the destination
*/
void compact(final ConstPool cp) {
name = cp.addUtf8Info(getName());
descriptor = cp.addUtf8Info(getDescriptor());
attribute = AttributeInfo.copyAll(attribute, cp);
constPool = cp;
}
void prune(final ConstPool cp) {
final ArrayList newAttributes = new ArrayList();
AttributeInfo invisibleAnnotations
= getAttribute(AnnotationsAttribute.invisibleTag);
if (invisibleAnnotations != null) {
invisibleAnnotations = invisibleAnnotations.copy(cp, null);
newAttributes.add(invisibleAnnotations);
}
AttributeInfo visibleAnnotations
= getAttribute(AnnotationsAttribute.visibleTag);
if (visibleAnnotations != null) {
visibleAnnotations = visibleAnnotations.copy(cp, null);
newAttributes.add(visibleAnnotations);
}
AttributeInfo parameterInvisibleAnnotations
= getAttribute(ParameterAnnotationsAttribute.invisibleTag);
if (parameterInvisibleAnnotations != null) {
parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null);
newAttributes.add(parameterInvisibleAnnotations);
}
AttributeInfo parameterVisibleAnnotations
= getAttribute(ParameterAnnotationsAttribute.visibleTag);
if (parameterVisibleAnnotations != null) {
parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null);
newAttributes.add(parameterVisibleAnnotations);
}
final AnnotationDefaultAttribute defaultAttribute
= (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag);
if (defaultAttribute != null) {
newAttributes.add(defaultAttribute);
}
final ExceptionsAttribute ea = getExceptionsAttribute();
if (ea != null) {
newAttributes.add(ea);
}
AttributeInfo signature
= getAttribute(SignatureAttribute.tag);
if (signature != null) {
signature = signature.copy(cp, null);
newAttributes.add(signature);
}
attribute = newAttributes;
name = cp.addUtf8Info(getName());
descriptor = cp.addUtf8Info(getDescriptor());
constPool = cp;
}
/**
* Returns a method name.
*/
public String getName() {
if (cachedName == null) {
cachedName = constPool.getUtf8Info(name);
}
return cachedName;
}
/**
* Sets a method name.
*/
public void setName(final String newName) {
name = constPool.addUtf8Info(newName);
cachedName = newName;
}
/**
* Returns true if this is not a constructor or a class initializer (static
* initializer).
*/
public boolean isMethod() {
final String n = getName();
return !n.equals(nameInit) && !n.equals(nameClinit);
}
/**
* Returns a constant pool table used by this method.
*/
public ConstPool getConstPool() {
return constPool;
}
/**
* Returns true if this is a constructor.
*/
public boolean isConstructor() {
return getName().equals(nameInit);
}
/**
* Returns true if this is a class initializer (static initializer).
*/
public boolean isStaticInitializer() {
return getName().equals(nameClinit);
}
/**
* Returns access flags.
*
* @see AccessFlag
*/
public int getAccessFlags() {
return accessFlags;
}
/**
* Sets access flags.
*
* @see AccessFlag
*/
public void setAccessFlags(final int acc) {
accessFlags = acc;
}
/**
* Returns a method descriptor.
*
* @see Descriptor
*/
public String getDescriptor() {
return constPool.getUtf8Info(descriptor);
}
/**
* Sets a method descriptor.
*
* @see Descriptor
*/
public void setDescriptor(final String desc) {
if (!desc.equals(getDescriptor())) {
descriptor = constPool.addUtf8Info(desc);
}
}
/**
* Appends an attribute. If there is already an attribute with the same
* name, the new one substitutes for it.
*
* @see #getAttributes()
*/
public void addAttribute(final AttributeInfo info) {
if (attribute == null) {
attribute = new ArrayList();
}
AttributeInfo.remove(attribute, info.getName());
attribute.add(info);
}
/**
* Returns an Exceptions attribute.
*
* @return an Exceptions attribute or null if it is not specified.
*/
public ExceptionsAttribute getExceptionsAttribute() {
final AttributeInfo info = AttributeInfo.lookup(attribute,
ExceptionsAttribute.tag);
return (ExceptionsAttribute)info;
}
/**
* Returns a Code attribute.
*
* @return a Code attribute or null if it is not specified.
*/
public CodeAttribute getCodeAttribute() {
final AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag);
return (CodeAttribute)info;
}
/**
* Removes an Exception attribute.
*/
public void removeExceptionsAttribute() {
AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
}
/**
* Adds an Exception attribute.
*
*
* The added attribute must share the same constant pool table as this
* method_info
structure.
*/
public void setExceptionsAttribute(final ExceptionsAttribute cattr) {
removeExceptionsAttribute();
if (attribute == null) {
attribute = new ArrayList();
}
attribute.add(cattr);
}
/**
* Removes a Code attribute.
*/
public void removeCodeAttribute() {
AttributeInfo.remove(attribute, CodeAttribute.tag);
}
/**
* Adds a Code attribute.
*
*
* The added attribute must share the same constant pool table as this
* method_info
structure.
*/
public void setCodeAttribute(final CodeAttribute cattr) {
removeCodeAttribute();
if (attribute == null) {
attribute = new ArrayList();
}
attribute.add(cattr);
}
// /**
// * Rebuilds a stack map table if the class file is for Java 6
// * or later. Java 5 or older Java VMs do not recognize a stack
// * map table. If doPreverify
is true, this method
// * also rebuilds a stack map for J2ME (CLDC).
// *
// * @param pool used for making type hierarchy.
// * @param cf rebuild if this class file is for Java 6 or later.
// * @see #rebuildStackMap(ClassPool)
// * @see #rebuildStackMapForME(ClassPool)
// * @since 3.6
// */
// public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
// throws BadBytecode
// {
// if (cf.getMajorVersion() >= ClassFile.JAVA_6)
// rebuildStackMap(pool);
//
// if (doPreverify)
// rebuildStackMapForME(pool);
// }
//
// /**
// * Rebuilds a stack map table. If no stack map table is included,
// * a new one is created. If this MethodInfo
does not
// * include a code attribute, nothing happens.
// *
// * @param pool used for making type hierarchy.
// * @see StackMapTable
// * @since 3.6
// */
// public void rebuildStackMap(ClassPool pool) throws BadBytecode {
// CodeAttribute ca = getCodeAttribute();
// if (ca != null) {
// StackMapTable smt = MapMaker.make(pool, this);
// ca.setAttribute(smt);
// }
// }
// /**
// * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included,
// * a new one is created. If this MethodInfo
does not
// * include a code attribute, nothing happens.
// *
// * @param pool used for making type hierarchy.
// * @see StackMapTable
// * @since 3.12
// */
// public void rebuildStackMapForME(ClassPool pool) throws BadBytecode {
// CodeAttribute ca = getCodeAttribute();
// if (ca != null) {
// StackMap sm = MapMaker.make2(pool, this);
// ca.setAttribute(sm);
// }
// }
/**
* Returns the line number of the source line corresponding to the specified
* bytecode contained in this method.
*
* @param pos
* the position of the bytecode (>= 0). an index into the code
* array.
* @return -1 if this information is not available.
*/
public int getLineNumber(final int pos) {
final CodeAttribute ca = getCodeAttribute();
if (ca == null) {
return -1;
}
final LineNumberAttribute ainfo = (LineNumberAttribute)ca
.getAttribute(LineNumberAttribute.tag);
if (ainfo == null) {
return -1;
}
return ainfo.toLineNumber(pos);
}
/**
* Changes a super constructor called by this constructor.
*
*
* This method modifies a call to super()
, which should be
* at the head of a constructor body, so that a constructor in a different
* super class is called. This method does not change actual parameters.
* Hence the new super class must have a constructor with the same signature
* as the original one.
*
*
* This method should be called when the super class of the class declaring
* this method is changed.
*
*
* This method does not perform anything unless this MethodInfo
* represents a constructor.
*
* @param superclass
* the new super class
*/
public void setSuperclass(final String superclass) throws BadBytecode {
if (!isConstructor()) {
return;
}
final CodeAttribute ca = getCodeAttribute();
final byte[] code = ca.getCode();
final CodeIterator iterator = ca.iterator();
final int pos = iterator.skipSuperConstructor();
if (pos >= 0) { // not this()
final ConstPool cp = constPool;
final int mref = X_Byte.readU16bit(code, pos + 1);
final int nt = cp.getMethodrefNameAndType(mref);
final int sc = cp.addClassInfo(superclass);
final int mref2 = cp.addMethodrefInfo(sc, nt);
X_Byte.write16bit(mref2, code, pos + 1);
}
}
/**
* @throws BadBytecode
*/
private void read(final MethodInfo src, final String methodname, final Map classnames)
throws BadBytecode {
final ConstPool destCp = constPool;
accessFlags = src.accessFlags;
name = destCp.addUtf8Info(methodname);
cachedName = methodname;
final ConstPool srcCp = src.constPool;
final String desc = srcCp.getUtf8Info(src.descriptor);
final String desc2 = Descriptor.rename(desc, classnames);
descriptor = destCp.addUtf8Info(desc2);
attribute = new ArrayList();
final ExceptionsAttribute eattr = src.getExceptionsAttribute();
if (eattr != null) {
attribute.add(eattr.copy(destCp, classnames));
}
final CodeAttribute cattr = src.getCodeAttribute();
if (cattr != null) {
attribute.add(cattr.copy(destCp, classnames));
}
}
private void read(final DataInput in) throws IOException {
accessFlags = in.readUnsignedShort();
name = in.readUnsignedShort();
descriptor = in.readUnsignedShort();
final int n = in.readUnsignedShort();
attribute = new ArrayList();
for (int i = 0; i < n; ++i) {
attribute.add(AttributeInfo.read(constPool, in));
}
}
void write(final DataOutputStream out) throws IOException {
out.writeShort(accessFlags);
out.writeShort(name);
out.writeShort(descriptor);
if (attribute == null) {
out.writeShort(0);
} else {
out.writeShort(attribute.size());
AttributeInfo.writeAll(attribute, out);
}
}
}