是否可以使用 ByteBuddy 拦截数组构造函数?
Is it possible to intercept array constructor with ByteBuddy?
我有一个回归测试,我试图计算数组实例化的数量。具体来说,我有兴趣计算 new int[..]
被调用了多少次。我整理了以下基于 ByteBuddy 建议的工具,我相信它们应该捕获 all 构造函数,但这不适用于数组(任何类型):
public class InstanceCounter {
public static void load() {
ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(Collections.singletonMap("foo.bar.BootstrapState", readBytes(InstanceCounter.class.getClassLoader().getResource("foo/bar/BootstrapState.class"))));
new AgentBuilder.Default()
.ignore(none())
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORMATION)
.with(InitializationStrategy.NoOp.INSTANCE)
.with(TypeStrategy.Default.REDEFINE)
.type(any())
.transform(new Transformer() {
@Override
public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
return builder.visit(Advice.to(InstanceCounter.class).on(any()));
}})
.installOn(ByteBuddyAgent.install());
}
@Advice.OnMethodEnter
public static void enter(final @Advice.Origin Class<?> origin) {
if (!BootstrapState.trace || BootstrapState.lock)
return;
BootstrapState.lock = true;
System.out.println(">>> " + origin);
BootstrapState.lock = false;
}
}
public class InstanceCounterTest {
@Test
public void test() {
InstanceCounter.load();
System.out.println("Creating array...");
BootstrapState.trace = true;
final Object[] y = new Object[3];
BootstrapState.trace = false;
System.out.println("Printing array: " + y);
}
}
当我运行这个测试时,我得到以下结果:
Creating array...
Printing array: [Ljava.lang.Object;@7f5eae0f
如果我将 final Object[] y = new Object[3]
更改为 new Object()
,则输出为:
Creating array...
>>> class java.lang.Object
Printing array: [Ljava.lang.Object;@68562ad5
我已经尽可能开放了建议检测(至少我认为我可以做到的尽可能开放),但这仍然没有检测数组对象的构造。
甚至可以检测数组对象的构造吗?
更新2020-07-28
As per the context of the original question, the provided answers express that ByteBuddy does not have direct support for automatic instrumentation of new int[]
. However, for those interested in an alternate solution, I was able to achieve this kind of instrumentation with Byteman.
数组没有构造函数;它们是使用 newarray
(对于原语)或 anewarray
(对于引用类型)创建的(如果您想发挥创意,还有 multianewarray
)。
如果您想修改数组创建,则必须找到一些方法来检测这些字节码指令。
常规对象构造的检测涉及将调用注入相关 class 的构造函数。这是可行的,因为每个 class 至少声明了一个构造函数,并且因为在对象创建期间总是调用构造函数。
相比之下,数组没有构造函数,因此无法注入调用。这就解释了为什么您对所有构造函数的检测都没有获取数组创建。
假设地,检测数组创建的另一种方法是找到所有使用 3 个“newarray”字节码的地方,并在它们之前进行检测调用。
如前所述,您无法更改数组 classes,因为它们未在 class 文件中表示。相反,您需要拦截它们的创建,这需要一些字节码解析。
Byte Buddy 公开了 ASM,它使用它自己来完成这种工作,但它比拦截构造函数要多得多的手动工作。
我有一个回归测试,我试图计算数组实例化的数量。具体来说,我有兴趣计算 new int[..]
被调用了多少次。我整理了以下基于 ByteBuddy 建议的工具,我相信它们应该捕获 all 构造函数,但这不适用于数组(任何类型):
public class InstanceCounter {
public static void load() {
ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(Collections.singletonMap("foo.bar.BootstrapState", readBytes(InstanceCounter.class.getClassLoader().getResource("foo/bar/BootstrapState.class"))));
new AgentBuilder.Default()
.ignore(none())
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORMATION)
.with(InitializationStrategy.NoOp.INSTANCE)
.with(TypeStrategy.Default.REDEFINE)
.type(any())
.transform(new Transformer() {
@Override
public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
return builder.visit(Advice.to(InstanceCounter.class).on(any()));
}})
.installOn(ByteBuddyAgent.install());
}
@Advice.OnMethodEnter
public static void enter(final @Advice.Origin Class<?> origin) {
if (!BootstrapState.trace || BootstrapState.lock)
return;
BootstrapState.lock = true;
System.out.println(">>> " + origin);
BootstrapState.lock = false;
}
}
public class InstanceCounterTest {
@Test
public void test() {
InstanceCounter.load();
System.out.println("Creating array...");
BootstrapState.trace = true;
final Object[] y = new Object[3];
BootstrapState.trace = false;
System.out.println("Printing array: " + y);
}
}
当我运行这个测试时,我得到以下结果:
Creating array...
Printing array: [Ljava.lang.Object;@7f5eae0f
如果我将 final Object[] y = new Object[3]
更改为 new Object()
,则输出为:
Creating array...
>>> class java.lang.Object
Printing array: [Ljava.lang.Object;@68562ad5
我已经尽可能开放了建议检测(至少我认为我可以做到的尽可能开放),但这仍然没有检测数组对象的构造。
甚至可以检测数组对象的构造吗?
更新2020-07-28
As per the context of the original question, the provided answers express that ByteBuddy does not have direct support for automatic instrumentation of
new int[]
. However, for those interested in an alternate solution, I was able to achieve this kind of instrumentation with Byteman.
数组没有构造函数;它们是使用 newarray
(对于原语)或 anewarray
(对于引用类型)创建的(如果您想发挥创意,还有 multianewarray
)。
如果您想修改数组创建,则必须找到一些方法来检测这些字节码指令。
常规对象构造的检测涉及将调用注入相关 class 的构造函数。这是可行的,因为每个 class 至少声明了一个构造函数,并且因为在对象创建期间总是调用构造函数。
相比之下,数组没有构造函数,因此无法注入调用。这就解释了为什么您对所有构造函数的检测都没有获取数组创建。
假设地,检测数组创建的另一种方法是找到所有使用 3 个“newarray”字节码的地方,并在它们之前进行检测调用。
如前所述,您无法更改数组 classes,因为它们未在 class 文件中表示。相反,您需要拦截它们的创建,这需要一些字节码解析。
Byte Buddy 公开了 ASM,它使用它自己来完成这种工作,但它比拦截构造函数要多得多的手动工作。