1 package annotations.io.classfile; 2 3 import org.objectweb.asm.ClassAdapter; 4 import org.objectweb.asm.ClassReader; 5 import org.objectweb.asm.ClassVisitor; 6 import org.objectweb.asm.Handle; 7 import org.objectweb.asm.Label; 8 import org.objectweb.asm.MethodAdapter; 9 import org.objectweb.asm.MethodVisitor; 10 import org.objectweb.asm.Opcodes; 11 12 import annotations.io.DebugWriter; 13 import annotations.util.AbstractClassVisitor; 14 15 public class CodeOffsetAdapter extends ClassAdapter { 16 static final DebugWriter debug; 17 final ClassReader cr; 18 final char[] buf; 19 int methodStart; 20 int codeStart; 21 int offset; 22 23 static { 24 debug = new DebugWriter(); 25 debug.setEnabled(false); 26 } 27 28 public CodeOffsetAdapter(ClassReader cr) { 29 this(cr, new AbstractClassVisitor()); 30 } 31 32 public CodeOffsetAdapter(ClassReader cr, ClassVisitor v) { 33 super(v); 34 this.cr = cr; 35 // const pool size is (not lowest) upper bound of string length 36 buf = new char[cr.header]; 37 methodStart = cr.header + 6; 38 methodStart += 4 + 2 * cr.readUnsignedShort(methodStart); 39 for (int i = cr.readUnsignedShort(methodStart-2); i > 0; --i) { 40 methodStart += 8; 41 for (int j = cr.readUnsignedShort(methodStart-2); j > 0; --j) { 42 methodStart += 6 + cr.readInt(methodStart+2); 43 } 44 } 45 methodStart += 2; 46 } 47 48 @Override 49 public MethodVisitor visitMethod(int access, 50 String name, String desc, 51 String signature, String[] exceptions) { 52 MethodVisitor v = 53 super.visitMethod(access, name, desc, signature, exceptions); 54 return new MethodAdapter(v) { 55 private int methodEnd; 56 57 { 58 String name = cr.readUTF8(methodStart + 2, buf); 59 String desc = cr.readUTF8(methodStart + 4, buf); 60 int attrCount = cr.readUnsignedShort(methodStart + 6); 61 debug.debug("visiting %s%s (%d)%n", name, desc, methodStart); 62 debug.debug("%d attributes%n", attrCount); 63 methodEnd = methodStart + 8; 64 65 // find code attribute 66 codeStart = methodEnd; 67 if (attrCount > 0) { 68 while (--attrCount >= 0) { 69 String attrName = cr.readUTF8(codeStart, buf); 70 debug.debug("attribute %s%n", attrName); 71 if ("Code".equals(attrName)) { 72 codeStart += 6; 73 offset = codeStart + cr.readInt(codeStart - 4); 74 codeStart += 8; 75 while (--attrCount >= 0) { 76 debug.debug("attribute %s%n", cr.readUTF8(offset, buf)); 77 offset += 6 + cr.readInt(offset + 2); 78 } 79 methodEnd = offset; 80 break; 81 } 82 codeStart += 6 + cr.readInt(codeStart + 2); 83 methodEnd = codeStart; 84 } 85 } 86 offset = 0; 87 } 88 89 private int readInt(int i) { 90 return cr.readInt(codeStart + i); 91 } 92 93 @Override 94 public void visitFieldInsn(int opcode, 95 String owner, String name, String desc) { 96 super.visitFieldInsn(opcode, owner, name, desc); 97 debug.debug("%d visitFieldInsn(%d, %s, %s, %s)%n", offset, 98 opcode, owner, name, desc); 99 offset += 3; 100 } 101 102 @Override 103 public void visitIincInsn(int var, int increment) { 104 super.visitIincInsn(var, increment); 105 debug.debug("%d visitIincInsn(%d, %d)%n", offset, var, increment); 106 offset += 3; 107 } 108 109 @Override 110 public void visitInsn(int opcode) { 111 super.visitInsn(opcode); 112 debug.debug("%d visitInsn(%d)%n", offset, opcode); 113 ++offset; 114 } 115 116 @Override 117 public void visitIntInsn(int opcode, int operand) { 118 super.visitIntInsn(opcode, operand); 119 debug.debug("%d visitIntInsn(%d, %d)%n", offset, opcode, operand); 120 offset += opcode == Opcodes.SIPUSH ? 3 : 2; 121 } 122 123 @Override 124 public void visitInvokeDynamicInsn(String name, String desc, 125 Handle bsm, Object... bsmArgs) { 126 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 127 debug.debug("%d visitInvokeDynamicInsn(%s, %s)%n", offset, 128 name, desc, bsm, bsmArgs); 129 offset += 5; 130 } 131 132 @Override 133 public void visitJumpInsn(int opcode, Label label) { 134 super.visitJumpInsn(opcode, label); 135 debug.debug("%d visitJumpInsn(%d, %s)%n", offset, opcode, label); 136 // account for wide instructions goto_w (200) and jsr_w (201) 137 offset += cr.readByte(codeStart + offset) < 200 ? 3 : 4; 138 assert offset > 0 && methodEnd > codeStart + offset; 139 } 140 141 @Override 142 public void visitLdcInsn(Object cst) { 143 super.visitLdcInsn(cst); 144 debug.debug("%d visitLdcInsn(%s)%n", offset, cst); 145 // account for wide instructions ldc_w (19) and ldc2_w (20) 146 offset += cr.readByte(codeStart + offset) > 18 ? 3 : 2; 147 assert offset > 0 && methodEnd > codeStart + offset; 148 } 149 150 @Override 151 public void visitLookupSwitchInsn(Label dflt, int[] keys, 152 Label[] labels) { 153 super.visitLookupSwitchInsn(dflt, keys, labels); 154 debug.debug("%d visitLookupSwitchInsn(%s)%n", offset, 155 dflt, keys, labels); 156 offset += 8 - (offset & 3); 157 offset += 4 + 8 * readInt(offset); 158 assert offset > 0 && methodEnd > codeStart + offset; 159 } 160 161 @Override 162 public void visitMethodInsn(int opcode, 163 String owner, String name, String desc) { 164 super.visitMethodInsn(opcode, owner, name, desc); 165 debug.debug("%d visitMethodInsn(%d, %s, %s, %s)%n", offset, 166 opcode, owner, name, desc); 167 offset += opcode == Opcodes.INVOKEINTERFACE ? 5 : 3; 168 } 169 170 @Override 171 public void visitMultiANewArrayInsn(String desc, int dims) { 172 super.visitMultiANewArrayInsn(desc, dims); 173 debug.debug("%d visitMultiANewArrayInsn(%s, %d)%n", offset, 174 desc, dims); 175 offset += 4; 176 } 177 178 @Override 179 public void visitTableSwitchInsn(int min, int max, 180 Label dflt, Label[] labels) { 181 super.visitTableSwitchInsn(min, max, dflt, labels); 182 debug.debug("%d visitTableSwitchInsn(%d, %d, %s)%n", offset, 183 min, max, dflt, labels); 184 offset += 8 - (offset & 3); 185 offset += 4 * (readInt(offset + 4) - readInt(offset) + 3); 186 assert offset > 0 && methodEnd > codeStart + offset; 187 } 188 189 @Override 190 public void visitTypeInsn(int opcode, String desc) { 191 super.visitTypeInsn(opcode, desc); 192 debug.debug("%d visitTypeInsn(%d, %s)%n", offset, opcode, desc); 193 offset += 3; 194 } 195 196 @Override 197 public void visitVarInsn(int opcode, int var) { 198 super.visitVarInsn(opcode, var); 199 debug.debug("%d visitVarInsn(%d, %d)%n", offset, opcode, var); 200 offset += var < 4 ? 1 : 2; 201 } 202 203 @Override 204 public void visitEnd() { 205 methodStart = methodEnd; 206 } 207 }; 208 } 209 210 public int getMethodCodeOffset() { return offset; } 211 212 public int getBytecodeOffset() { return codeStart + offset; } 213 } 214