public
Created

ASM bytecode transformation to identify concrete class at runtime

  • Download Gist
MethodWeaver.groovy
Groovy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
package org.flightofstairs.honours.capture.agent
 
import org.flightofstairs.honours.common.Call
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.slf4j.LoggerFactory
import org.objectweb.asm.tree.*
import org.objectweb.asm.tree.analysis.*
 
class MethodWeaver extends MethodNode implements Opcodes {
 
private final String owner
private final MethodVisitor mv
 
private final ArrayList<MethodInsnNode> calls = new ArrayList<MethodInsnNode>();
 
MethodWeaver(String owner, int access, String name, String desc,
String signature, String[] exceptions, MethodVisitor mv) {
super(access, name, desc, signature, exceptions);
this.owner = owner;
this.mv = mv;
}
 
@Override
void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
 
if(opcode == INVOKEVIRTUAL) {
int probeID = Tracer.INSTANCE.probes.createProbeIDAt(new Call(owner, name, desc))
 
super.visitFieldInsn(GETSTATIC, "org/flightofstairs/honours/capture/agent/Tracer", "INSTANCE", "Lorg/flightofstairs/honours/capture/agent/Tracer;")
super.visitLdcInsn(probeID)
super.visitMethodInsn(INVOKEVIRTUAL, "org/flightofstairs/honours/capture/agent/Tracer", "probe", "(I)V")
}
 
super.visitMethodInsn(opcode, owner, name, desc);
 
if(opcode==INVOKEINTERFACE) {
calls.add((MethodInsnNode) instructions.getLast());
}
 
 
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 5 * calls.size(), maxLocals)
}
@Override
public void visitEnd() {
if(!calls.isEmpty()) {
try {
List<AbstractInsnNode> objectRefLoadPoints = []
 
Analyzer analyzer = new Analyzer(new SourceInterpreter())
Frame[] frames = analyzer.analyze(owner, this) as Frame[]
 
for (MethodInsnNode methodInsnNode : calls) {
Frame frame = frames[instructions.indexOf(methodInsnNode)]
 
if(frame == null) continue
 
int stackSlot = frame.getStackSize() - 1
 
for(Type type : Type.getArgumentTypes(methodInsnNode.desc)) {
stackSlot -= type.getSize()
}
 
SourceValue stackValue = (SourceValue) frame.getStack(stackSlot)
objectRefLoadPoints.addAll stackValue.insns
}
 
for(AbstractInsnNode node : objectRefLoadPoints) {
instructions.insert(node, traceInstructions(owner, "lol"))
}
} catch (AnalyzerException ex) {
LoggerFactory.getLogger(getClass()).warn("Error instrumenting method: {} {}", owner, name, ex)
}
}
 
accept(mv)
 
LoggerFactory.getLogger(getClass()).trace("Instrumented method: {} {}", owner, name)
}
 
private static InsnList traceInstructions(String owner, String method) {
InsnList list = new InsnList();
 
list.add(new InsnNode(Opcodes.DUP))
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"))
list.add(new FieldInsnNode(Opcodes.GETSTATIC, "org/flightofstairs/honours/capture/agent/Tracer", "INSTANCE", "Lorg/flightofstairs/honours/capture/agent/Tracer;"))
list.add(new InsnNode(Opcodes.SWAP))
list.add(new LdcInsnNode(owner))
list.add(new LdcInsnNode(method))
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "org/flightofstairs/honours/capture/agent/Tracer", "probe", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V"))
 
return list
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.