ByteBuddy - 重基本机方法

ByteBuddy - Rebasing native methods

我正在检查是否可以使用 ByteBuddy 将一些笨重的 ASM 代码替换为更小、更优雅的解决方案。一部分是 "wrap" 本机方法,这意味着在 class 中为任何现有的本机方法添加前缀,并提供一个新的、具有相同名称和签名的非本机方法调用前缀方法。所有这些都可以在构建期间发生,所以我不需要任何代理,也不需要担心潜在的 class 架构更改等

从文档来看,这似乎是一个变基案例,其中 ByteBuddy 保留原始方法的副本并提供通过 @SuperCall 调用它的可能性。

除了不能影响原始方法的新名称(它们需要有前缀,而不是后缀)之外,以下代码导致以下错误:

        new ByteBuddy()
        .rebase(typePool.describe("test.ClassWithNatives").resolve(), ClassFileLocator.ForClassLoader.ofClassPath())
        .method(named("someNativeMethod"))
        .intercept(MethodDelegation.to(NativeInterceptor.class))
        .make()
        .load(getClass().getClassLoader())
        .getLoaded();

.

Exception in thread "main" java.lang.IllegalStateException: Error invoking java.lang.ClassLoader#findClass
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Direct.defineClass(ClassInjector.java:410)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.inject(ClassInjector.java:183)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:187)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:120)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:79)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4376)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4366)
    at ByteBuddyTest.wrapNatives(ByteBuddyTest.java:45)
    at ByteBuddyTest.start(ByteBuddyTest.java:22)
    at ByteBuddyTest.main(ByteBuddyTest.java:16)
Caused by: java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file ClassWithNatives
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Direct.defineClass(ClassInjector.java:406)
    ... 9 more

有没有可能用ByteBuddy实现这样的场景?

这只有通过 Instrumentation 使用 Java 代理才有可能。否则,JVM 不知道如何变基具有必须遵循方法命名约定的本地适配器的本地方法。为了让 Byte Buddy 工作,原始代码被移动到一个不同的方法,以便从原始方法调用,否则是不可能的。

使用 Java 代理,可以 运行:

new AgentBuilder.Default()
  .enableNativeMethodPrefix("foo")
  .type(named("test.ClassWithNatives"))
  .transform((b,t,c,m) -> b
    .method(named("someNativeMethod"))
    .intercept(MethodDelegation.to(NativeInterceptor.class))
  ).installOn(inst);