我正在使用ASM并希望重写类似的内容:
someMethod().targetMethod(args...)
为:
someMethod().injectedMethod(arg).targetMethod(args...)
麻烦的是,我不知道之前的方法是什么,我只知道目标方法(所以找到someMethod()
并且之后注入不是一个选项)。
我也有很多版本的目标方法,我希望这个参数可以使用不同的参数集。
使用ASM我可以很容易地找到目标方法调用,但不幸的是,那时的操作数堆栈是:
[ argN, ..., arg1, instance, ... ]
虽然我可以计算出实例的距离,但是我没有可以注入的字节码会读取它。我知道你可以使用带有dup命令的技巧来最多4个参数,但我需要一个通用的解决方案。
我可以添加一堆局部变量并将所有内容复制到堆栈中,复制指向的实例并重新打开所有内容,但这是我真正不想要的运行时效率低下。
我认为可行的是,如果我可以跟踪哪个指令负责将实例指针放在堆栈上,然后我可以在那里注入我的方法调用而不是在目标方法调用。但是我没有找到任何帮助我做这件事的运气。
我知道像AspectJ这样的东西允许这样做,但是因为加载而且AspectJ太慢了所以必须为很多类做这个。
有人能指出我在ASM之上构建的分析工具可能让我这样做,还是有人能想到一种更好的方法来在另一种方法之前注入一个方法调用?
答案 0 :(得分:2)
如果我理解你的问题,我会以不同的方式实现你想做的事情。
使用ASM事件驱动的字节码修改,我首先将someMethod(arg,arg,arg)重命名为copyOf_someMethod(arg,arg,arg)。然后我创建了一个名为someMethod(arg,arg,arg)的新方法,它进行了一些处理,然后调用了copyOf_someMethod(arg,arg,arg)。
我在我实现的ClassVisitor的visitMethod(..)方法中重命名了方法:
MethodVisitor methodVisitor =
super.visitMethod(
methodAccess, "copyOf_" + methodName, methodDesc,
methodSignature, methodExceptions );
return methodVisitor;
在visitMethod(..)中,我还将所有方法签名详细信息存储在可以在visitEnd()方法中使用的类变量中。
我实际上将方法详细信息存储在MethodDetail对象中并将其放在队列中:
private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>();
我使用我实现的ClassVisitor的visitEnd()方法创建了someMethod(arg,arg,arg)的新实现。我使用ASMFier生成代码以放入visitEnd()方法。实现使用了我之前在visitMethod(..)中存储的细节。新实现进行了一些处理,然后调用copyOf_someMethod()。在visitEnd()方法中,我弹出了队列的所有MethodDetail,以及我之前由ASMFier生成的名为ASM代码的每个MethodDetail。
使用这种设计,我为一个方法创建了一个代理,该方法进行了一些处理,然后调用了原始方法。请注意,原始方法已重命名为copyOf_someMethod(..)。另请注意,我为原始方法提供了一个新实现,该方法充当代理。
为了支持多个参数,我使用ASMFier为1个arg,2个arg,3个arg,e.t.c生成不同的代码。如果被代理的方法有超过7个参数,我最多支持7个参数并抛出Unsupported Exception。在visitEnd(..)方法中,我根据原始方法有多少方法参数调用了不同的代码(由ASMFier生成)。
我使用javaagent拦截类加载并修改字节。
由于我是ASM的新手,也许我没有正确理解你的问题 - 但是如果你的问题是创建一个代理进行一些处理,然后调用原始方法,那么我的解决方案就可以了。它似乎并不慢。具有代理方法的类的类加载时间并不比没有字节代码修改慢得多。引入代码的运行速度并不慢,ASMFier生成的ASM代码似乎非常快。
干杯
答案 1 :(得分:1)
通常情况下,您可以将堆栈中的值卸载到临时局部变量中。来自ASM commons包的LocalVariableSorter适配器使其变得非常简单。但实际上,有超过4个参数的方法是一种罕见的情况。无论如何,它在运行时进行全面的数据流分析仍然更简单,更可靠。
ASM的org.objectweb.asm.tree.analysis为数据流分析提供了便利,例如您可以使用SourceInterpreter来跟踪在每个变量和堆栈插槽上生成值的指令。有关详细信息,请参阅ASM User Guide。
答案 2 :(得分:0)
查看org.objectweb.asm.tree.analysis。 SourceIterpreter应该为您提供将值放入堆栈的指令。