Итак, я пытаюсь сгенерировать относительно простой код с помощью Byte Buddy, но постоянно сталкиваюсь с исключениями.
По сути, я пытаюсь, чтобы Byte Buddy (версия 1.9.0) генерировал эквивалент следующего класса Java (это всего лишь упрощенный пример, проблема обычно возникает при попытке передать вновь созданный объект методу):
public class CalendarSetter
{
public void setCalendarTime(Calendar calendar)
{
calendar.setTime(new Date());
}
}
Код Byte Buddy, который я придумал, выглядит следующим образом (с использованием синтаксиса Xtend, но он довольно близок к на Яву):
val Builder<?> builder = new ByteBuddy()
.subclass(Object).name("CalendarSetter").merge(Visibility.PUBLIC)
.defineMethod("setCalendarTime", void, Visibility.PUBLIC)
.withParameter(Calendar)
.intercept(MethodCall.invoke(new ForLoadedMethod(Calendar.getDeclaredMethod("setTime", Date)))
.onArgument(0)
.withMethodCall(MethodCall.construct(Date.getConstructor)))
builder.make.load(class.classLoader).loaded
К сожалению, это приводит только к следующему исключению:
java.lang.IllegalStateException: Cannot assign public java.util.Date() to java.util.Date arg0
at net.bytebuddy.implementation.MethodCall$ArgumentLoader$ForMethodCall.resolve(MethodCall.java:1470)
at net.bytebuddy.implementation.MethodCall.toStackManipulation(MethodCall.java:2397)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2434)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:698)
...
Некоторые дальнейшие исследования в отладчике позволяют предположить, что это связано с тем, что конструктор для Date
(как и все конструкторы) имеет возвращаемый тип void
, который, в свою очередь, не совместим по присваиванию с java.util.Date
. VoidAwareAssigner
Byte Buddy выдает недопустимое StackManipulation
, потому что исходный тип void
, тогда как целевой тип не void
, а Typing
не является динамическим. Я ожидаю, что VoidAwareAssigner
действительно должен знать, что конструкторы по сути являются void
методами, но я, вероятно, упускаю здесь что-то еще.
Каков правильный способ передачи вновь созданного объекта в качестве аргумента метода в Byte Buddy?
ОБНОВЛЕНИЕ: мне удалось избежать исключения, добавив .withAssigner(custom, Typing.DYNAMIC)
с пользовательским "тривиальным" Assigner
, который всегда возвращает StackManipulation.Trivial
. Этот обходной путь создает, казалось бы, правильный (и рабочий) байт-код:
public class CalendarSetter {
public void setCalendarTime(java.util.Calendar);
Code:
0: aload_1
1: new #8 // class java/util/Date
4: dup
5: invokespecial #12 // Method java/util/Date."<init>":()V
8: invokevirtual #18 // Method java/util/Calendar.setTime:(Ljava/util/Date;)V
11: return
...
Однако у меня все еще есть ощущение, что это не совсем правильный подход и может упрощать некоторые сценарии, о которых я не знаю...