Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
11 KiB
442 lines
11 KiB
/**
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package EDU.purdue.cs.bloat.file;
|
|
|
|
import java.io.*;
|
|
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
|
|
/**
|
|
* Code is used to store the Code attribute of a method in a class file. The
|
|
* Code attribute stores the raw bytecode of the method, the maximum stack
|
|
* height and maximum number of locals used by the method, and the exception
|
|
* handlers used in the method. Code may have several attributes. The local
|
|
* variable table and the line number table are modeled explicitly. All other
|
|
* attributes are modeled as generic attributes.
|
|
*
|
|
* @see EDU.purdue.cs.bloat.reflect.Catch Catch
|
|
* @see GenericAttribute
|
|
* @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo
|
|
* @see LocalVariableTable
|
|
*
|
|
* @author Nate Nystrom (<a
|
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
|
|
*/
|
|
public class Code extends Attribute {
|
|
private ClassInfo classInfo;
|
|
|
|
private int maxStack;
|
|
|
|
private int maxLocals;
|
|
|
|
private byte[] code;
|
|
|
|
private Catch[] handlers;
|
|
|
|
private LineNumberTable lineNumbers;
|
|
|
|
private LocalVariableTable locals;
|
|
|
|
private Attribute[] attrs;
|
|
|
|
/**
|
|
* Constructor for creating a <code>Code</code> from scratch
|
|
*
|
|
* @param codeIndex
|
|
* The index in the constant pool for the UTF8 "Code"
|
|
*/
|
|
Code(final ClassInfo classInfo, final int codeIndex) {
|
|
// Don't know length!
|
|
super(codeIndex, -1);
|
|
|
|
this.classInfo = classInfo;
|
|
|
|
// These should get set during a commit
|
|
this.maxStack = -1;
|
|
this.maxLocals = -1;
|
|
this.code = new byte[0];
|
|
this.handlers = new Catch[0];
|
|
this.lineNumbers = null; // It's okay for these to be null
|
|
this.locals = null;
|
|
this.attrs = new Attribute[0];
|
|
}
|
|
|
|
/**
|
|
* Constructor. Create a Code attribute from a data stream.
|
|
*
|
|
* @param in
|
|
* The data stream containing the class file.
|
|
* @param index
|
|
* The index into the constant pool of the name of the attribute.
|
|
* @param len
|
|
* The length of the attribute, excluding the header.
|
|
* @exception IOException
|
|
* If an error occurs while reading.
|
|
*/
|
|
public Code(final ClassInfo classInfo, final DataInputStream in,
|
|
final int index, final int len) throws IOException {
|
|
super(index, len);
|
|
|
|
this.classInfo = classInfo;
|
|
|
|
maxStack = in.readUnsignedShort();
|
|
maxLocals = in.readUnsignedShort();
|
|
|
|
final int codeLength = in.readInt();
|
|
|
|
code = new byte[codeLength];
|
|
|
|
for (int read = 0; read < codeLength;) {
|
|
read += in.read(code, read, codeLength - read);
|
|
}
|
|
|
|
final int numHandlers = in.readUnsignedShort();
|
|
|
|
handlers = new Catch[numHandlers];
|
|
|
|
for (int i = 0; i < numHandlers; i++) {
|
|
handlers[i] = readCatch(in);
|
|
}
|
|
|
|
final int numAttributes = in.readUnsignedShort();
|
|
|
|
attrs = new Attribute[numAttributes];
|
|
|
|
for (int i = 0; i < numAttributes; i++) {
|
|
final int nameIndex = in.readUnsignedShort();
|
|
final int length = in.readInt();
|
|
|
|
final Constant name = classInfo.constants()[nameIndex];
|
|
|
|
if (name != null) {
|
|
if ("LineNumberTable".equals(name.value())) {
|
|
lineNumbers = new LineNumberTable(in, nameIndex, length);
|
|
attrs[i] = lineNumbers;
|
|
} else if ("LocalVariableTable".equals(name.value())) {
|
|
locals = new LocalVariableTable(in, nameIndex, length);
|
|
attrs[i] = locals;
|
|
}
|
|
}
|
|
|
|
if (attrs[i] == null) {
|
|
attrs[i] = new GenericAttribute(in, nameIndex, length);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write the attribute to a data stream.
|
|
*
|
|
* @param out
|
|
* The data stream of the class file.
|
|
* @exception IOException
|
|
* If an error occurs while writing.
|
|
*/
|
|
public void writeData(final DataOutputStream out) throws IOException {
|
|
out.writeShort(maxStack);
|
|
out.writeShort(maxLocals);
|
|
|
|
out.writeInt(code.length);
|
|
out.write(code, 0, code.length);
|
|
|
|
out.writeShort(handlers.length);
|
|
|
|
for (int i = 0; i < handlers.length; i++) {
|
|
writeCatch(out, handlers[i]);
|
|
}
|
|
|
|
out.writeShort(attrs.length);
|
|
|
|
for (int i = 0; i < attrs.length; i++) {
|
|
out.writeShort(attrs[i].nameIndex());
|
|
out.writeInt(attrs[i].length());
|
|
attrs[i].writeData(out);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read an exception handler attribute.
|
|
*
|
|
* @param in
|
|
* The data stream of the class file.
|
|
* @return A Catch attribute for the handler.
|
|
* @exception IOException
|
|
* If an error occurs while reading.
|
|
*/
|
|
private Catch readCatch(final DataInputStream in) throws IOException {
|
|
final int startPC = in.readUnsignedShort();
|
|
final int endPC = in.readUnsignedShort();
|
|
final int handlerPC = in.readUnsignedShort();
|
|
final int catchType = in.readUnsignedShort();
|
|
|
|
return new Catch(startPC, endPC, handlerPC, catchType);
|
|
}
|
|
|
|
/**
|
|
* Write an exception handler attribute.
|
|
*
|
|
* @param out
|
|
* The data stream of the class file.
|
|
* @param c
|
|
* A Catch attribute for the handler.
|
|
* @exception IOException
|
|
* If an error occurs while writing.
|
|
*/
|
|
private void writeCatch(final DataOutputStream out, final Catch c)
|
|
throws IOException {
|
|
final int startPC = c.startPC();
|
|
final int endPC = c.endPC();
|
|
final int handlerPC = c.handlerPC();
|
|
final int catchType = c.catchTypeIndex();
|
|
|
|
out.writeShort(startPC);
|
|
out.writeShort(endPC);
|
|
out.writeShort(handlerPC);
|
|
out.writeShort(catchType);
|
|
}
|
|
|
|
/**
|
|
* Set the maximum height of the operand stack used by the code.
|
|
*
|
|
* @param maxStack
|
|
* The maximum height of the stack.
|
|
*/
|
|
public void setMaxStack(final int maxStack) {
|
|
this.maxStack = maxStack;
|
|
}
|
|
|
|
/**
|
|
* Set the maximum number of locals used by the code.
|
|
*
|
|
* @param maxLocals
|
|
* The maximum number of locals.
|
|
*/
|
|
public void setMaxLocals(final int maxLocals) {
|
|
this.maxLocals = maxLocals;
|
|
}
|
|
|
|
/**
|
|
* Get the maximum height of the operand stack used by the code.
|
|
*
|
|
* @return The maximum number of locals.
|
|
*/
|
|
public int maxStack() {
|
|
return maxStack;
|
|
}
|
|
|
|
/**
|
|
* Get the maximum number of locals used by the code.
|
|
*
|
|
* @return The maximum number of locals.
|
|
*/
|
|
public int maxLocals() {
|
|
return maxLocals;
|
|
}
|
|
|
|
/**
|
|
* Set the exception handlers in the method.
|
|
*
|
|
* @param handlers
|
|
* The handlers.
|
|
*/
|
|
public void setExceptionHandlers(final Catch[] handlers) {
|
|
this.handlers = handlers;
|
|
}
|
|
|
|
/**
|
|
* Get the length of the attribute.
|
|
*
|
|
* @return The length of the attribute.
|
|
*/
|
|
public int length() {
|
|
int length = 2 + 2 + 4 + code.length + 2 + handlers.length * 8 + 2;
|
|
|
|
for (int i = 0; i < attrs.length; i++) {
|
|
length += 2 + 4 + attrs[i].length();
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
/**
|
|
* Get the line number debug info for the code.
|
|
*
|
|
* @return The line number debug info for the code.
|
|
*/
|
|
public LineNumberDebugInfo[] lineNumbers() {
|
|
if (lineNumbers != null) {
|
|
return lineNumbers.lineNumbers();
|
|
}
|
|
|
|
return new LineNumberDebugInfo[0];
|
|
}
|
|
|
|
/**
|
|
* Get the local variable debug info for the code.
|
|
*
|
|
* @return The local variable debug info for the code.
|
|
*/
|
|
public LocalDebugInfo[] locals() {
|
|
if (locals != null) {
|
|
return locals.locals();
|
|
}
|
|
|
|
return new LocalDebugInfo[0];
|
|
}
|
|
|
|
/**
|
|
* Set the line number debug info for the code.
|
|
*
|
|
* @param lineNumbers
|
|
* The line number debug info for the code.
|
|
*/
|
|
public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) {
|
|
if (lineNumbers == null) {
|
|
for (int i = 0; i < attrs.length; i++) {
|
|
if (this.lineNumbers == attrs[i]) {
|
|
final Attribute[] a = attrs;
|
|
attrs = new Attribute[a.length - 1];
|
|
System.arraycopy(a, 0, attrs, 0, i);
|
|
System.arraycopy(a, i + 1, attrs, i, attrs.length - i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.lineNumbers = null;
|
|
} else if (this.lineNumbers != null) {
|
|
this.lineNumbers.setLineNumbers(lineNumbers);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the local variable debug info for the code.
|
|
*
|
|
* @param locals
|
|
* The local variable debug info for the code.
|
|
*/
|
|
public void setLocals(final LocalDebugInfo[] locals) {
|
|
if (locals == null) {
|
|
for (int i = 0; i < attrs.length; i++) {
|
|
if (this.locals == attrs[i]) {
|
|
final Attribute[] a = attrs;
|
|
attrs = new Attribute[a.length - 1];
|
|
System.arraycopy(a, 0, attrs, 0, i);
|
|
System.arraycopy(a, i + 1, attrs, i, attrs.length - i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.locals = null;
|
|
} else if (this.locals != null) {
|
|
this.locals.setLocals(locals);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the exception handlers in the method.
|
|
*
|
|
* @return The handlers.
|
|
*/
|
|
public Catch[] exceptionHandlers() {
|
|
return handlers;
|
|
}
|
|
|
|
/**
|
|
* Get the bytes of the code.
|
|
*
|
|
* @return The code.
|
|
*/
|
|
public byte[] code() {
|
|
return code;
|
|
}
|
|
|
|
/**
|
|
* Return the length of the code array
|
|
*/
|
|
public int codeLength() {
|
|
return (code.length);
|
|
}
|
|
|
|
/**
|
|
* Set the bytes of the code.
|
|
*
|
|
* @param code
|
|
* The code.
|
|
*/
|
|
public void setCode(final byte[] code) {
|
|
this.code = code;
|
|
}
|
|
|
|
/**
|
|
* Private constructor for cloning.
|
|
*/
|
|
private Code(final Code other) {
|
|
super(other.nameIndex, other.length);
|
|
|
|
this.classInfo = other.classInfo;
|
|
this.maxStack = other.maxStack;
|
|
this.maxLocals = other.maxLocals;
|
|
|
|
this.code = new byte[other.code.length];
|
|
System.arraycopy(other.code, 0, this.code, 0, other.code.length);
|
|
this.handlers = new Catch[other.handlers.length];
|
|
for (int i = 0; i < other.handlers.length; i++) {
|
|
this.handlers[i] = (Catch) other.handlers[i].clone();
|
|
}
|
|
|
|
if (other.lineNumbers != null) {
|
|
this.lineNumbers = (LineNumberTable) other.lineNumbers.clone();
|
|
}
|
|
|
|
if (other.locals != null) {
|
|
this.locals = (LocalVariableTable) other.locals.clone();
|
|
}
|
|
|
|
this.attrs = new Attribute[other.attrs.length];
|
|
for (int i = 0; i < other.attrs.length; i++) {
|
|
this.attrs[i] = other.attrs[i];
|
|
}
|
|
}
|
|
|
|
public Object clone() {
|
|
return (new Code(this));
|
|
}
|
|
|
|
/**
|
|
* Returns a string representation of the attribute.
|
|
*/
|
|
public String toString() {
|
|
String x = "";
|
|
|
|
if (handlers != null) {
|
|
for (int i = 0; i < handlers.length; i++) {
|
|
x += "\n " + handlers[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* for (int i = 0; i < attrs.length; i++) { x += "\n " + attrs[i]; }
|
|
*/
|
|
|
|
return "(code " + maxStack + " " + maxLocals + " " + code.length + x
|
|
+ ")";
|
|
}
|
|
}
|
|
|