/*
* 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.attribute;
import java.util.ArrayList;
import java.util.List;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.cojen.classfile.Attribute;
import org.cojen.classfile.AttributeFactory;
import org.cojen.classfile.CodeBuffer;
import org.cojen.classfile.ConstantPool;
import org.cojen.classfile.ExceptionHandler;
import org.cojen.classfile.LocalVariable;
import org.cojen.classfile.Location;
import org.cojen.classfile.MethodInfo;
/**
* This class corresponds to the Code_attribute structure as defined in
* section 4.7.4 of <i>The Java Virtual Machine Specification</i>.
* To make it easier to create bytecode for the CodeAttr, use the
* CodeBuilder.
*
* @author Brian S O'Neill
* @see org.cojen.classfile.Opcode
* @see org.cojen.classfile.CodeBuilder
*/
public class CodeAttr extends Attribute {
private CodeBuffer mCodeBuffer;
private List<Attribute> mAttributes = new ArrayList<Attribute>(2);
private LineNumberTableAttr mLineNumberTable;
private LocalVariableTableAttr mLocalVariableTable;
private LineNumberTableAttr mOldLineNumberTable;
private LocalVariableTableAttr mOldLocalVariableTable;
private StackMapTableAttr mOldStackMapTable;
private StackMapTableAttr mStackMapTable;
public CodeAttr(ConstantPool cp) {
super(cp, CODE);
}
public CodeAttr(ConstantPool cp, String name) {
super(cp, name);
}
public CodeAttr(ConstantPool cp, String name, int length,
DataInput din, AttributeFactory attrFactory)
throws IOException
{
super(cp, name);
final int maxStackDepth = din.readUnsignedShort();
final int maxLocals = din.readUnsignedShort();
final byte[] byteCodes = new byte[din.readInt()];
din.readFully(byteCodes);
int exceptionHandlerCount = din.readUnsignedShort();
final ExceptionHandler[] handlers =
new ExceptionHandler[exceptionHandlerCount];
for (int i=0; i<exceptionHandlerCount; i++) {
handlers[i] = ExceptionHandler.readFrom(cp, din);
}
mCodeBuffer = new CodeBuffer() {
public int getMaxStackDepth() {
return maxStackDepth;
}
public int getMaxLocals() {
return maxLocals;
}
public byte[] getByteCodes() {
return byteCodes.clone();
}
public ExceptionHandler[] getExceptionHandlers() {
return handlers.clone();
}
};
int attributeCount = din.readUnsignedShort();
for (int i=0; i<attributeCount; i++) {
addAttribute(Attribute.readFrom(cp, din, attrFactory));
}
}
/**
* Returns null if no CodeBuffer is defined for this CodeAttr.
*/
public CodeBuffer getCodeBuffer() {
return mCodeBuffer;
}
/**
* As a side effect of calling this method, new line number and local
* variable tables are created.
*/
public void setCodeBuffer(CodeBuffer code) {
mCodeBuffer = code;
mOldLineNumberTable = mLineNumberTable;
mOldLocalVariableTable = mLocalVariableTable;
mOldStackMapTable = mStackMapTable;
mAttributes.remove(mLineNumberTable);
mAttributes.remove(mLocalVariableTable);
mAttributes.remove(mStackMapTable);
mLineNumberTable = null;
mLocalVariableTable = null;
mStackMapTable = null;
}
/**
* Returns the line number in the source code from the given bytecode
* address (start_pc).
*
* @return -1 if no line number is mapped for the start_pc.
*/
public int getLineNumber(Location start) {
LineNumberTableAttr table = mOldLineNumberTable;
if (table == null) {
table = mLineNumberTable;
}
if (table == null || start.getLocation() < 0) {
return -1;
} else {
return table.getLineNumber(start);
}
}
/**
* Returns local variable info at the given location, for the given number.
*
* @return null if unknown
*/
public LocalVariable getLocalVariable(Location useLocation, int number) {
int useLoc = useLocation.getLocation();
if (useLoc < 0) {
return null;
} else {
return getLocalVariable(useLoc, number);
}
}
/**
* Returns local variable info at the given location, for the given number.
*
* @return null if unknown
*/
public LocalVariable getLocalVariable(int useLocation, int number) {
LocalVariableTableAttr table = mOldLocalVariableTable;
if (table == null) {
table = mLocalVariableTable;
}
if (table == null) {
return null;
} else {
return table.getLocalVariable(useLocation, number);
}
}
/**
* Map a bytecode address (start_pc) to a line number in the source code
* as a debugging aid.
*/
public void mapLineNumber(Location start, int line_number) {
if (mLineNumberTable == null) {
addAttribute(new LineNumberTableAttr(getConstantPool()));
}
mLineNumberTable.addEntry(start, line_number);
}
/**
* Indicate a local variable's use information be recorded in the
* ClassFile as a debugging aid. If the LocalVariable doesn't provide
* both a start and end location, then its information is not recorded.
* This method should be called at most once per LocalVariable instance.
*/
public void localVariableUse(LocalVariable localVar) {
if (mLocalVariableTable == null) {
addAttribute(new LocalVariableTableAttr(getConstantPool()));
}
mLocalVariableTable.addEntry(localVar);
}
public StackMapTableAttr getStackMapTable() {
return mStackMapTable;
}
public void initialStackMapFrame(MethodInfo method) {
if (mStackMapTable == null) {
// FIXME: add one?
} else {
mStackMapTable.initialStackMapFrame(method);
}
}
public void addAttribute(Attribute attr) {
if (attr instanceof LineNumberTableAttr) {
if (mLineNumberTable != null) {
mAttributes.remove(mLineNumberTable);
}
mLineNumberTable = (LineNumberTableAttr)attr;
} else if (attr instanceof LocalVariableTableAttr) {
if (mLocalVariableTable != null) {
mAttributes.remove(mLocalVariableTable);
}
mLocalVariableTable = (LocalVariableTableAttr)attr;
} else if (attr instanceof StackMapTableAttr) {
if (mStackMapTable != null) {
mAttributes.remove(mStackMapTable);
}
mStackMapTable = (StackMapTableAttr)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 = 12;
if (mCodeBuffer != null) {
length += mCodeBuffer.getByteCodes().length;
ExceptionHandler[] handlers = mCodeBuffer.getExceptionHandlers();
if (handlers != null) {
length += 8 * handlers.length;
}
}
int size = mAttributes.size();
for (int i=0; i<size; i++) {
length += mAttributes.get(i).getLength();
length += 6; // attributes have an intial 6 byte length
}
return length;
}
public void writeDataTo(DataOutput dout) throws IOException {
if (mCodeBuffer == null) {
throw new IllegalStateException("CodeAttr has no CodeBuffer set");
}
ExceptionHandler[] handlers = mCodeBuffer.getExceptionHandlers();
dout.writeShort(mCodeBuffer.getMaxStackDepth());
dout.writeShort(mCodeBuffer.getMaxLocals());
byte[] byteCodes = mCodeBuffer.getByteCodes();
dout.writeInt(byteCodes.length);
dout.write(byteCodes);
if (handlers != null) {
int exceptionHandlerCount = handlers.length;
dout.writeShort(exceptionHandlerCount);
for (int i=0; i<exceptionHandlerCount; i++) {
handlers[i].writeTo(dout);
}
} else {
dout.writeShort(0);
}
int size = mAttributes.size();
dout.writeShort(size);
for (int i=0; i<size; i++) {
Attribute attr = mAttributes.get(i);
attr.writeTo(dout);
}
mOldLineNumberTable = null;
mOldLocalVariableTable = null;
mOldStackMapTable = null;
}
}