/*
* 03/21/2010
*
* Copyright (C) 2010 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSTALanguageSupport.License.txt file for details.
*/
package org.fife.rsta.ac.java.classreader.attributes;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.fife.rsta.ac.java.classreader.*;
/**
* A variable-length attribute used in the attributes table of
* {@link MethodInfo} structures. A <code>Code</code> attribute contains the
* JVM instructions and auxiliary information for a single method, instance
* initialization method, or class or interface initialization method. Every
* JVM implementation must recognize <code>Code</code> attributes. If the
* method is either <code>native</code> or <code>abstract</code>, its
* <code>MethodInfo</code> structure must not have a <code>Code</code>
* attribute. Otherwise, its <code>MethodInfo</code> structure must have
* exactly one <code>Code</code> attribute.
*
* @author Robert Futrell
* @version 1.0
*/
public class Code extends AttributeInfo {
/**
* The parent method.
*/
private MethodInfo mi;
/**
* The maximum depth of the operand stack of this method at any point
* during its execution.
*/
private int maxStack;
/**
* The number of local variables in the local variable array allocated
* upon invocation of this method, including the local variables used to
* pass parameters to the method on invocation.<p>
*
* The greatest local variable index for a value of type <code>long</code>
* or <code>double</code> is <code>maxLocals-2</code>. The greatest local
* variable index for a value of any other type is <code>maxLocals-1</code>.
*/
private int maxLocals;
// /**
// * The actual bytes of JVM code that implement the method. This must have
// * length greater than zero.
// */
// private int[] code;
/**
* The size of the method's code, in bytes.
*/
private int codeLength;
/**
* The exception handlers in the <code>code</code> array. The order of
* the handlers in this table is significant.
*/
private ExceptionTableEntry[] exceptionTable;
/**
* The names of parameters to the parent method, if debugging was enabled
* during compilation.
* @see #LOCAL_VARIABLE_TABLE
*/
private String[] paramNames;
/**
* Attributes of this <code>Code</code> attribute.
*/
private List attributes;
private static final String LINE_NUMBER_TABLE = "LineNumberTable";
private static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable";
private static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable";
private static final String STACK_MAP_TABLE = "StackMapTable";
/**
* Constructor.
*
* @param mi Information on the parent method.
*/
public Code(MethodInfo mi) {
super(mi.getClassFile());
this.mi = mi;
}
// /**
// * Returns the code byte at the specified offset.
// *
// * @param offset The offset.
// * @return The byte.
// */
// public int getByte(int offset) {
// return code[offset];
// }
/**
* Returns the length of the code array, in bytes.
*
* @return The length of the code array.
*/
public int getCodeLength() {
return codeLength;//code.length;
}
/**
* Returns the number of local variables in the local variable array
* allocated upon invocation of this method, including the local variables
* used to pass parameters to the method on invocation.<p>
*
* The greatest local variable index for a value of type <code>long</code>
* or <code>double</code> is <code>maxLocals-2</code>. The greatest local
* variable index for a value of any other type is <code>maxLocals-1</code>.
*
* @return the maximum size of the local variable array.
*/
public int getMaxLocals() {
return maxLocals;
}
/**
* Returns the maximum depth of the operand stack of this method at any
* point during its execution.
*/
public int getMaxStack() {
return maxStack;
}
/**
* Returns the method containing this code.
*
* @return The method containing this code.
*/
public MethodInfo getMethodInfo() {
return mi;
}
/**
* If debugging was enabled during compilation, this method returns the
* name of the given parameter to this method. Otherwise, <code>null</code>
* is returned.
*
* @param index The index of the parameter.
* @return The name of the parameter, or <code>null</code>.
*/
public String getParameterName(int index) {
return paramNames==null ? null : paramNames[index];
}
/**
* Reads a <code>Code</code> attribute from an input stream.
*
* @param mi The parent method.
* @param in The input stream.
* @return The <code>Code</code> attribute.
* @throws IOException If an IO error occurs.
*/
public static Code read(MethodInfo mi, DataInputStream in)
throws IOException {
Code code = new Code(mi);
code.maxStack = in.readUnsignedShort();
code.maxLocals = in.readUnsignedShort();
code.codeLength = in.readInt();
Util.skipBytes(in, code.codeLength);
int exceptionTableLength = in.readUnsignedShort();
if (exceptionTableLength>0) {
code.exceptionTable = new ExceptionTableEntry[exceptionTableLength];
for (int i=0; i<exceptionTableLength; i++) {
ExceptionTableEntry ete = ExceptionTableEntry.read(
mi.getClassFile(), in);
code.exceptionTable[i] = ete;
}
}
int attrCount = in.readUnsignedShort();
if (attrCount>0) {
code.attributes = new ArrayList(1); // Usually just 1 or 2
for (int i=0; i<attrCount; i++) {
AttributeInfo ai = code.readAttribute(in);
if (ai!=null) { // Not one handled "custom"
code.attributes.add(ai);
}
}
}
return code;
}
/**
* Reads an attribute for this <code>Code</code> attribute from an input
* stream.
*
* @param in The input stream to read from.
* @return The attribute read.
* @throws IOException If an IO error occurs.
*/
private AttributeInfo readAttribute(DataInputStream in) throws IOException {
AttributeInfo ai = null;
ClassFile cf = mi.getClassFile();
int attributeNameIndex = in.readUnsignedShort();
int attributeLength = in.readInt();
String attrName = cf.getUtf8ValueFromConstantPool(attributeNameIndex);
// The line number table is more useful to a debugger than to us.
if (LINE_NUMBER_TABLE.equals(attrName)) { // 4.7.12
//String name = mi.getName(true) + ".<code>";
//System.out.println(name + ": Attribute " + attrName + " currently ignored");
Util.skipBytes(in, attributeLength);
//ai = null;
}
// Describes a local variable during execution of this code. We only
// use it to grab the names of method parameters.
else if (LOCAL_VARIABLE_TABLE.equals(attrName)) { // 4.7.13
// If this attribute is defined, then this class was compiled with
// debugging enabled! We can grab the names of the method
// parameters, to make code completion a little nicer. Note that
// we only grab the names of parameters, not all local variables,
// for speed and space.
int paramCount = mi.getParameterCount();
paramNames = new String[paramCount];
boolean isStatic = mi.isStatic();
int localVariableTableLength = in.readUnsignedShort();
for (int i=0; i<localVariableTableLength; i++) {
/*int startPC = */in.readUnsignedShort();
/*int length = */in.readUnsignedShort();
int nameIndex = in.readUnsignedShort();
/*int descriptorIndex = */in.readUnsignedShort();
// Non-static methods have implicit "this" variable passed in,
// so we must avoid that
int index = in.readUnsignedShort();
int adjustedIndex = isStatic ? index : index-1;
if (adjustedIndex>=0 && adjustedIndex<paramNames.length) {
String name = cf.getUtf8ValueFromConstantPool(nameIndex);
//System.out.println("!!! " + getClassFile().
// getClassName(false) + "." + getMethodInfo().
// getNameAndParameters() + " - " + index + ": " + name);
paramNames[adjustedIndex] = name;
}
}
}
// We don't care about LocalVariableTypeTables
else if (LOCAL_VARIABLE_TYPE_TABLE.equals(attrName)) { // 4.7.14
Util.skipBytes(in, attributeLength);
}
// Currently skip StackMapTables also
else if (STACK_MAP_TABLE.equals(attrName)) { // 4.7.4
Util.skipBytes(in, attributeLength);
}
else {
System.out.println("Unsupported Code attribute: " + attrName);
ai = AttributeInfo.readUnsupportedAttribute(cf, in, attrName,
attributeLength);
}
return ai;
}
}