Код Byte Buddy для передачи вновь созданного объекта в метод

Итак, я пытаюсь сгенерировать относительно простой код с помощью 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
...

Однако у меня все еще есть ощущение, что это не совсем правильный подход и может упрощать некоторые сценарии, о которых я не знаю...


person raner    schedule 05.06.2020    source источник


Ответы (1)


Вы нашли ошибку, которую я только что исправил в основной ветке. Это будет частью выпуска 1.10.12.

Byte Buddy неправильно определил возвращаемый тип конструктора как void.

person Rafael Winterhalter    schedule 07.06.2020