2017-01-24 5 views
1

Я проверяю, могу ли я использовать ByteBuddy для замены какого-нибудь громоздкого кода ASM меньшим, более элегантным решением. Одна часть состоит в том, чтобы «обернуть» собственные методы, то есть префикс любого существующего нативного метода в классе и предоставить новый, неродный метод с тем же именем и подписью, вызывающий префиксный. Все это может произойти во время сборки, поэтому мне не нужны какие-либо агенты, не нужно беспокоиться о возможных изменениях схемы классов и т. Д.ByteBuddy - Rebasing native методы

Из документации это похоже на случай переустановки, когда 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?

ответ

2

Это возможно только с помощью агента Java через Instrumentation. В противном случае JVM не знает, как переустановить собственный метод, который имеет встроенные адаптеры, которые должны следовать соглашениям об именах методов. Для работы Byte Buddy исходный код переносится на другой метод для того, чтобы быть invokable из исходного метода, который невозможен.

Использование агента 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); 
+0

Может быть, я что-то отсутствует, но для меня это приводит к той же ошибки. Я увеличил лямбда, чтобы получить «ReceiverTypeDefinition» и сохранил 'byte []' в файле, прежде чем возвращать определение. Из байтового кода я увидел, что флаг «native» удален из списка модификаторов класса, но не добавлен код для «Interceptor». Также не существует переименованного нативного метода (кроме того, что префикс native-метода, передаваемый ByteBuddy, кажется, используется только для инструментария, но на самом деле не влияет на то, как ByteBuddy генерирует новые имена методов, не так ли?) –

+0

Вы правы, я просто нашел здесь ошибку, которую я разрешаю в версии 1.6.6. Я подталкиваю его в следующие часы. –

+0

Приятно, я только что проверил. Работает с агентом сейчас! Также через 'new ByteBuddy()' собственный метод теперь перехвачен правильно, просто потому, что я, очевидно, не могу вызвать исходный метод, потому что он не префикс. Не уверен, что это сломает архитектуру, но было бы так «неправильно» иметь что-то вроде «нового ByteBuddy(). EnableNativeMethodPrefix (« prefix »)'? Также я был бы признателен, если бы вы могли поделиться некоторыми сведениями о том, как на самом деле вызывается prefixed native, поскольку я не смог найти собственный метод, используя 'javap', просто' invokestatic'. Но где теперь определяется метод? –