Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does Byte Buddy have an API to help convert regular Java code into ASM syntax? #1745

Open
qujsh opened this issue Jan 9, 2025 · 6 comments
Assignees
Labels
Milestone

Comments

@qujsh
Copy link

qujsh commented Jan 9, 2025

Does Byte Buddy have an API to help convert regular Java code into ASM syntax, or do you have any other suggestions? I now want to add execution time logging to every method call in a method. I haven't used ASM-related content before, and now I'm getting errors while writing ASM code myself. If I expand the functionality, I expect more errors. That's why I'm asking about the current issue. Example:

public class PersonTest {

    public void sayHello() {

        //For example, I want to use ASM to comment out these lines of code

        // long startTime = System.currentTimeMillis();
        System.out.println("Hello, world!");
        // long meathodExecutionTime = System.currentTimeMillis() - startTime;
        // System.out.println(userExecutionTime);

        // long startTime2 = System.currentTimeMillis();
        testLinePrint();
        // long meathodExecutionTime2 = System.currentTimeMillis() - startTime2;
        // System.out.println(userExecutionTime2);
    }

    public void testLinePrint() {
        System.out.println("testLinePrint");
    }
}

my error code: (T_T)
image

@raphw
Copy link
Owner

raphw commented Jan 9, 2025

ASM offers that, using ASMifier. You will only have to adjust the imports for using the same with Byte Buddy.

@raphw raphw self-assigned this Jan 9, 2025
@raphw raphw added the question label Jan 9, 2025
@raphw raphw added this to the 1.15.11 milestone Jan 9, 2025
@vsTianhao
Copy link

vsTianhao commented Jan 10, 2025

ASM操作比较繁琐但自由度很高,Byte Buddy都集成到一起了,操作简单但是很多地方很难自由发挥

@qujsh
Copy link
Author

qujsh commented Jan 10, 2025

ASM操作比较繁琐但自由度很高,Byte Buddy都集成到一起了,操作简单但是很多地方很难自由发挥

现在是准备能使用现成的就使用现成的,自己今天拼了下这儿的ASM指令,尤其是这儿的局部变量值和栈大小,直接错乱掉了;

比如:我希望给方法内的每个调用加上执行时间的打印;

    public String sayHello() {
        
        System.out.println("Hello, world!");  //这个前后
        int a = 1;
        return testLinePrint(a);  //这个前后
    }

得到结果:(错误的)

   public String sayHello() {
        PrintStream var10000 = System.out;

        long a = System.currentTimeMillis();
        var10000.println("Hello, world!");   //这个前后
        long var3 = System.currentTimeMillis();

        int a = 1;
        byte var10001 = a;
        a = System.currentTimeMillis();
        String var20 = this.testLinePrint(var10001);  //这个前后
        var3 = System.currentTimeMillis();

        return var20;
    }

转化代码:

     @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {

            System.out.println("owner: " + owner + ", name: " + name + ", descriptor: " + descriptor + ", isInterface: " + isInterface);

            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            mv.visitVarInsn(Opcodes.LSTORE, 1); // 保存开始时间到局部变量 1

            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);

            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            mv.visitVarInsn(Opcodes.LSTORE, 3); // 保存开始时间到局部变量 3
        }

简单场景可以自己手动控制这儿的 Opcodes.LSTORE值,如果我给做成功能 sayHello的结构体变复杂了,我感觉自己写的可能就不靠谱了;所以现在是想找找有没有现成的封装能使用的,比如 byte buddy 在 advice的 @Advice.OnMethodEnter 和 @Advice.OnMethodExit 实现中是怎么将代码正常嵌入进去的;

@qujsh
Copy link
Author

qujsh commented Jan 10, 2025

ASM offers that, using ASMifier. You will only have to adjust the imports for using the same with Byte Buddy.

Thank you for your reply, I tried the ASM.ASMifier tool, and it is really useful. .However, after testing, I found a problem. It's very easy for me to make mistakes when concatenating the local variable table and stack size, for example(Troublesome):

mv.visitVarInsn(Opcodes.LSTORE, ??? ); // what value 
mv.visitVarInsn(Opcodes.LLOAD, ???);

Or: 

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            System.out.println("maxStack: " + maxStack + ", maxLocals: " + maxLocals);
//            super.visitMaxs(10, 100);  //Fixed large value
            super.visitMaxs(???, ???);
        }

I realized that the @Advice.OnMethodEnter and @Advice.OnMethodExit I initially used for adding code before and after method execution are very useful, and I don't need to worry about setting the sizes of local variables. Now, my requirement is to add code logic before and after every method execution within a method, which is very similar to the previous @advice logic. So, I would like to ask if it's possible to migrate the internal logic of @advice to achieve similar functionality. How can it be implemented, or which part should I look into?

My program's goal:

  public String sayHello() {
        
        System.out.println("Hello, world!");  //这个前后
        int a = 1;
        return testLinePrint(a);  //这个前后
    }

To: 

 public String sayHello() {

        long startTime = System.currentTimeMillis();
        System.out.println("Hello, world!");
        long methodExecutionTime = System.currentTimeMillis() - startTime;
        System.out.println(methodExecutionTime);

        int a = 1;

        long startTime2 = System.currentTimeMillis();
        String b = testLinePrint(a);
        long meathodExecutionTime2 = System.currentTimeMillis() - startTime2;
        System.out.println(meathodExecutionTime2);
        return b;
    }

@raphw
Copy link
Owner

raphw commented Jan 12, 2025

Advice is a very complex component and I do not think it can be retrofitted. You want to concattendate ASMified code?

@dogourd
Copy link

dogourd commented Jan 13, 2025

Have you looked into MemberSubstitution? It might be helpful. Here's an example: it inspects all non-synthetic and non-constructor methods in a class and modifies all internal method invocations to redirect them to a specific method. Adjust the details according to your specific situation.

MemberSubstitution.relaxed().method(any()).replaceWithChain(
     Lists.newArrayList(MemberSubstitution.Substitution.Chain.Step.ForDelegation.withCustomMapping().to(method))
).on(not(isSynthetic().or(isConstructor())));

The above code will eventually return an AsmVisitorWrapper instance, which you should be able to integrate easily using ByteBuddy's DSL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants