11

我正在使用 ASM 并想重写如下内容:

someMethod().targetMethod(args...)

至:

someMethod().injectedMethod(arg).targetMethod(args...)

问题是我不知道之前的方法是什么,我只知道目标方法(因此someMethod()在此之后查找和注入不是一种选择)。

我也有许多版本的目标方法,我希望它使用不同的参数集。

使用 ASM 我可以很容易地找到目标方法调用,但不幸的是,此时的操作数堆栈是:

[ argN, ..., arg1, instance, ... ]

虽然我可以计算出实例将下降多远,但我无法注入可以读取它的字节码。我知道你最多可以使用 dup 命令的技巧来处理 4 个参数,但我需要一个通用的解决方案。

我可以添加一堆局部变量并从堆栈中复制所有内容,复制指向的实例并将所有内容放回原处,但这是我真的不想要的运行时效率低下。

我认为可行的是,如果我可以跟踪哪些指令负责将实例指针放在堆栈上,然后我可以将我的方法调用注入那里而不是目标方法调用。但是,我没有运气找到任何可以帮助我做到这一点的东西。

我知道像 AspectJ 这样的东西允许这样做,但是在加载很多类时必须这样做,而且 AspectJ 太慢了。

谁能指出基于 ASM 构建的分析工具可能让我这样做,或者任何人都可以想到一种更好的方法来在另一个方法调用之前注入一个方法调用?

4

3 回答 3

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,并为每个 MethodDetail 调用了我之前由 ASMFier 生成的 ASM 代码。

使用这种设计,我为一种方法创建了一个代理,该方法进行了一些处理,然后调用了原始方法。请注意,原始方法已重命名为 copyOf_someMethod(..)。另请注意,我为充当代理的原始方法提供了一个新实现。

为了支持多个参数,我使用 ASMFier 为 1 arg、2 arg、3 arg 等生成不同的代码,我最多支持 7 个参数,如果被代理的方法有超过 7 个参数,则抛出 Unsupported Exception。在 visitEnd(..) 方法中,我调用了不同的代码(由 ASMFier 生成),具体取决于原始方法有多少方法参数。

我使用 javaagent 来拦截类加载并修改字节。

由于我是 ASM 的新手,也许我没有正确理解您的问题 - 但是,如果您的问题是关于创建一个代理来进行一些处理然后调用原始方法,那么我的解决方案就可以了。它似乎并不慢。其方法被代理的类的类加载时间并不比没有修改字节码慢得多。引入代码的运行速度并不慢,ASMFier 生成的 ASM 代码看起来非常快。

干杯

于 2012-12-19T09:07:29.503 回答
1

在一般情况下,您可以将堆栈中的值卸载到临时局部变量中。ASM commons 包中的LocalVariableSorter适配器使它变得非常简单。但实际上具有超过 4 个参数的方法很少见。无论如何,它仍然比在运行时进行全面的数据流分析更简单、更可靠。

ASM 的org.objectweb.asm.tree.analysis为数据流分析提供了便利,例如,您可以使用SourceInterpreter跟踪哪些指令在每个变量和堆栈槽上产生了值。有关详细信息,请参阅ASM 用户指南。

于 2012-12-19T04:54:52.427 回答
0

查看 org.objectweb.asm.tree.analysis。SourceIterpreter 应该为您提供将值放入堆栈的指令。

于 2012-12-19T16:21:59.430 回答