package annotations.io.classfile;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import annotations.io.DebugWriter;
import annotations.util.AbstractClassVisitor;
public class CodeOffsetAdapter extends ClassAdapter {
static final DebugWriter debug;
final ClassReader cr;
final char[] buf;
int methodStart;
int codeStart;
int offset;
static {
debug = new DebugWriter();
debug.setEnabled(false);
}
public CodeOffsetAdapter(ClassReader cr) {
this(cr, new AbstractClassVisitor());
}
public CodeOffsetAdapter(ClassReader cr, ClassVisitor v) {
super(v);
this.cr = cr;
// const pool size is (not lowest) upper bound of string length
buf = new char[cr.header];
methodStart = cr.header + 6;
methodStart += 4 + 2 * cr.readUnsignedShort(methodStart);
for (int i = cr.readUnsignedShort(methodStart-2); i > 0; --i) {
methodStart += 8;
for (int j = cr.readUnsignedShort(methodStart-2); j > 0; --j) {
methodStart += 6 + cr.readInt(methodStart+2);
}
}
methodStart += 2;
}
@Override
public MethodVisitor visitMethod(int access,
String name, String desc,
String signature, String[] exceptions) {
MethodVisitor v =
super.visitMethod(access, name, desc, signature, exceptions);
return new MethodAdapter(v) {
private int methodEnd;
{
String name = cr.readUTF8(methodStart + 2, buf);
String desc = cr.readUTF8(methodStart + 4, buf);
int attrCount = cr.readUnsignedShort(methodStart + 6);
debug.debug("visiting %s%s (%d)%n", name, desc, methodStart);
debug.debug("%d attributes%n", attrCount);
methodEnd = methodStart + 8;
// find code attribute
codeStart = methodEnd;
if (attrCount > 0) {
while (--attrCount >= 0) {
String attrName = cr.readUTF8(codeStart, buf);
debug.debug("attribute %s%n", attrName);
if ("Code".equals(attrName)) {
codeStart += 6;
offset = codeStart + cr.readInt(codeStart - 4);
codeStart += 8;
while (--attrCount >= 0) {
debug.debug("attribute %s%n", cr.readUTF8(offset, buf));
offset += 6 + cr.readInt(offset + 2);
}
methodEnd = offset;
break;
}
codeStart += 6 + cr.readInt(codeStart + 2);
methodEnd = codeStart;
}
}
offset = 0;
}
private int readInt(int i) {
return cr.readInt(codeStart + i);
}
@Override
public void visitFieldInsn(int opcode,
String owner, String name, String desc) {
super.visitFieldInsn(opcode, owner, name, desc);
debug.debug("%d visitFieldInsn(%d, %s, %s, %s)%n", offset,
opcode, owner, name, desc);
offset += 3;
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn(var, increment);
debug.debug("%d visitIincInsn(%d, %d)%n", offset, var, increment);
offset += 3;
}
@Override
public void visitInsn(int opcode) {
super.visitInsn(opcode);
debug.debug("%d visitInsn(%d)%n", offset, opcode);
++offset;
}
@Override
public void visitIntInsn(int opcode, int operand) {
super.visitIntInsn(opcode, operand);
debug.debug("%d visitIntInsn(%d, %d)%n", offset, opcode, operand);
offset += opcode == Opcodes.SIPUSH ? 3 : 2;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc,
Handle bsm, Object... bsmArgs) {
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
debug.debug("%d visitInvokeDynamicInsn(%s, %s)%n", offset,
name, desc, bsm, bsmArgs);
offset += 5;
}
@Override
public void visitJumpInsn(int opcode, Label label) {
super.visitJumpInsn(opcode, label);
debug.debug("%d visitJumpInsn(%d, %s)%n", offset, opcode, label);
// account for wide instructions goto_w (200) and jsr_w (201)
offset += cr.readByte(codeStart + offset) < 200 ? 3 : 4;
assert offset > 0 && methodEnd > codeStart + offset;
}
@Override
public void visitLdcInsn(Object cst) {
super.visitLdcInsn(cst);
debug.debug("%d visitLdcInsn(%s)%n", offset, cst);
// account for wide instructions ldc_w (19) and ldc2_w (20)
offset += cr.readByte(codeStart + offset) > 18 ? 3 : 2;
assert offset > 0 && methodEnd > codeStart + offset;
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys,
Label[] labels) {
super.visitLookupSwitchInsn(dflt, keys, labels);
debug.debug("%d visitLookupSwitchInsn(%s)%n", offset,
dflt, keys, labels);
offset += 8 - (offset & 3);
offset += 4 + 8 * readInt(offset);
assert offset > 0 && methodEnd > codeStart + offset;
}
@Override
public void visitMethodInsn(int opcode,
String owner, String name, String desc) {
super.visitMethodInsn(opcode, owner, name, desc);
debug.debug("%d visitMethodInsn(%d, %s, %s, %s)%n", offset,
opcode, owner, name, desc);
offset += opcode == Opcodes.INVOKEINTERFACE ? 5 : 3;
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
super.visitMultiANewArrayInsn(desc, dims);
debug.debug("%d visitMultiANewArrayInsn(%s, %d)%n", offset,
desc, dims);
offset += 4;
}
@Override
public void visitTableSwitchInsn(int min, int max,
Label dflt, Label[] labels) {
super.visitTableSwitchInsn(min, max, dflt, labels);
debug.debug("%d visitTableSwitchInsn(%d, %d, %s)%n", offset,
min, max, dflt, labels);
offset += 8 - (offset & 3);
offset += 4 * (readInt(offset + 4) - readInt(offset) + 3);
assert offset > 0 && methodEnd > codeStart + offset;
}
@Override
public void visitTypeInsn(int opcode, String desc) {
super.visitTypeInsn(opcode, desc);
debug.debug("%d visitTypeInsn(%d, %s)%n", offset, opcode, desc);
offset += 3;
}
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var);
debug.debug("%d visitVarInsn(%d, %d)%n", offset, opcode, var);
offset += var < 4 ? 1 : 2;
}
@Override
public void visitEnd() {
methodStart = methodEnd;
}
};
}
public int getMethodCodeOffset() { return offset; }
public int getBytecodeOffset() { return codeStart + offset; }
}