I am trying (with no success) to print only the contents of a given method. The following code almost does the trick:
class MyTraceMethodVisitor extends MethodVisitor {
public MyTraceMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
}
}
class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (name.equals("get777"))
return new MyTraceMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
return null;
}
}
running it with
ClassReader classReader = new ClassReader("something.Point");
PrintWriter printWriter = new PrintWriter(System.out);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter);
MyClassVisitor myClassVisitor = new MyClassVisitor(traceClassVisitor);
classReader.accept(myClassVisitor, ClassReader.SKIP_DEBUG);
resulting in
// class version 50.0 (50)
// access flags 0x21
public class something/Point {
// access flags 0x1
public get777()I
SIPUSH 777
IRETURN
}
What I'd like to get was just
SIPUSH 777
IRETURN
without signature, comments and whatsoever. How can I accomplish that?
The answer is already pretty old and involves writing much code.
As of asm v5 printing method instructions is straightforward:
// Setup for asm ClassReader, ClassWriter and your implementation of the ClassVisitor(e.g.: YourClassVisitor)
final ClassReader reader = new ClassReader(classBytes);
final ClassWriter writer = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
final ClassVisitor visitor =new YourClassVisitor(Opcodes.ASM5, visitor);
In your implementation of the ClassVisitor, simply override the visitMethod method. Here an example:
public class YourClassVisitor extends ClassVisitor {
public InstrumentationClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
Printer p = new Textifier(Opcodes.ASM5) {
@Override
public void visitMethodEnd() {
print(new PrintWriter(System.out)); // print it after it has been visited
}
};
return new TraceMethodVisitor(mv, p);
}
}
The TraceMethodVisitor will receive the event calls for visiting method instructions etc. by the classVisitor. The code will then be printed by the TraceMethodVisitor with the help of the Printer.
This seems to do the trick.. although I don't understand how:
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
public class BytecodePrettyPrinter {
/**
* Gets us the bytecode method body of a given method.
* @param className The class name to search for.
* @param methodName The method name.
* @param methodDescriptor The method's descriptor.
* Can be null if one wishes to just get the first
* method with the given name.
* @throws IOException
*/
public static String[] getMethod(String className, String methodName, String methodDescriptor) throws IOException {
ClassReader classReader = new ClassReader(className);
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, new SourceCodeTextifier(), printWriter);
MethodSelectorVisitor methodSelectorVisitor = new MethodSelectorVisitor(traceClassVisitor, methodName, methodDescriptor);
classReader.accept(methodSelectorVisitor, ClassReader.SKIP_DEBUG);
return toList(stringWriter.toString());
}
/**
* Gets us the bytecode method body of a given method.
* @param className The class name to search for.
* @param methodName The method name.
* @throws IOException
*/
public static String[] getMethod(String className, String methodName) throws IOException {
return getMethod(className, methodName, null);
}
private static String[] toList(String str) {
//won't work correctly for all OSs
String[] operations = str.split("[" + "\n" + "]");
for (int i = 0; i < operations.length; ++i) {
operations[i] = operations[i].trim();
}
return operations;
}
private static class MethodSelectorVisitor extends ClassVisitor {
private final String methodName;
private final String methodDescriptor;
public MethodSelectorVisitor(ClassVisitor cv, String methodName, String methodDescriptor) {
super(Opcodes.ASM4, cv);
this.methodName = methodName;
this.methodDescriptor = methodDescriptor;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (methodName.equals(name)) {
if (methodDescriptor == null)
return new MaxVisitFilterMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
if (methodDescriptor.equals(desc))
return new MaxVisitFilterMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
}
return null;
}
}
private static class MaxVisitFilterMethodVisitor extends MethodVisitor {
public MaxVisitFilterMethodVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
}
}
private static class SourceCodeTextifier extends Printer {
public SourceCodeTextifier() {
this(Opcodes.ASM4);
}
protected SourceCodeTextifier(final int api) {
super(api);
}
@Override
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces)
{
}
@Override
public void visitSource(final String file, final String debug) {
}
@Override
public void visitOuterClass(
final String owner,
final String name,
final String desc)
{
}
@Override
public Textifier visitClassAnnotation(
final String desc,
final boolean visible)
{
return new Textifier();
}
@Override
public void visitClassAttribute(final Attribute attr) {
}
@Override
public void visitInnerClass(
final String name,
final String outerName,
final String innerName,
final int access)
{
}
@Override
public Textifier visitField(
final int access,
final String name,
final String desc,
final String signature,
final Object value)
{
return new Textifier();
}
@Override
public Textifier visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions)
{
Textifier t = new Textifier();
text.add(t.getText());
return t;
}
@Override
public void visitClassEnd() {
}
@Override
public void visit(final String name, final Object value) {
}
@Override
public void visitEnum(
final String name,
final String desc,
final String value)
{
}
@Override
public Textifier visitAnnotation(
final String name,
final String desc)
{
return new Textifier();
}
@Override
public Textifier visitArray(
final String name)
{
return new Textifier();
}
@Override
public void visitAnnotationEnd() {
}
@Override
public Textifier visitFieldAnnotation(
final String desc,
final boolean visible)
{
return new Textifier();
}
@Override
public void visitFieldAttribute(final Attribute attr) {
visitAttribute(attr);
}
@Override
public void visitFieldEnd() {
}
@Override
public Textifier visitAnnotationDefault() {
return new Textifier();
}
@Override
public Textifier visitMethodAnnotation(
final String desc,
final boolean visible)
{
return new Textifier();
}
@Override
public Textifier visitParameterAnnotation(
final int parameter,
final String desc,
final boolean visible)
{
return new Textifier();
}
@Override
public void visitMethodAttribute(final Attribute attr) {
}
@Override
public void visitCode() {
}
@Override
public void visitFrame(
final int type,
final int nLocal,
final Object[] local,
final int nStack,
final Object[] stack)
{
}
@Override
public void visitInsn(final int opcode) {
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
}
@Override
public void visitVarInsn(final int opcode, final int var) {
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
}
@Override
public void visitFieldInsn(
final int opcode,
final String owner,
final String name,
final String desc)
{
}
@Override
public void visitMethodInsn(
final int opcode,
final String owner,
final String name,
final String desc)
{
}
@Override
public void visitInvokeDynamicInsn(
String name,
String desc,
Handle bsm,
Object... bsmArgs)
{
}
@Override
public void visitJumpInsn(final int opcode, final Label label) {
}
@Override
public void visitLabel(final Label label) {
}
@Override
public void visitLdcInsn(final Object cst) {
}
@Override
public void visitIincInsn(final int var, final int increment) {
}
@Override
public void visitTableSwitchInsn(
final int min,
final int max,
final Label dflt,
final Label... labels)
{
}
@Override
public void visitLookupSwitchInsn(
final Label dflt,
final int[] keys,
final Label[] labels)
{
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
}
@Override
public void visitTryCatchBlock(
final Label start,
final Label end,
final Label handler,
final String type)
{
}
@Override
public void visitLocalVariable(
final String name,
final String desc,
final String signature,
final Label start,
final Label end,
final int index)
{
}
@Override
public void visitLineNumber(final int line, final Label start) {
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
}
@Override
public void visitMethodEnd() {
}
public void visitAttribute(final Attribute attr) {
}
}
}
and one can run it using:
@Test
public void someTest() throws IOException {
String[] ops = BytecodePrettyPrinter.getMethod("java.lang.String", "<init>", null);
for (String op : ops)
System.out.println(op);
}
In ASM 4, there is a new abstraction called Printer. you can pass your own Printer instance (e.g. extend or copy Textifier implementation) in constructor of the TraceClassVisitor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With