Reputation: 477
I'm checking if I can use ByteBuddy to replace some bulky ASM code with a smaller, more elegant solution. One part is to "wrap" native methods, meaning prefix any existing native method in a class and provide a new, non-native method with the same name and signature calling the prefixed one. All this can happen during build time, so I don't need any agents, neither need to worry about potential class schema changes, etc
From the documentation it seemed like a case for rebasing where ByteBuddy leaves a copy of the original method and provides the possibilty to call it via @SuperCall
.
Besides not being able to influence the new names for the original methods (they need to have a prefix, not a postfix) the following code leads to the error below:
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
Is it possible to realize such a scenario with ByteBuddy?
Upvotes: 1
Views: 1228
Reputation: 44077
This is only possible by using a Java agent via an Instrumentation
. Otherwise, the JVM does not know how to rebase a native method which has native adapters that have to follow method naming conventions. For Byte Buddy to work, the original code is moved to a different method for being invokable from the original method which is not possible otherwise.
Using a Java agent, it is possible to run:
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);
Upvotes: 3