Можно ли перехватить конструктор массива с помощью ByteBuddy? - PullRequest
1 голос
/ 11 июля 2020

У меня есть регрессионный тест, в котором я пытаюсь подсчитать количество экземпляров массивов. В частности, мне интересно посчитать, сколько раз вызывается new int[..]. Я собрал следующие инструменты, основанные на рекомендациях ByteBuddy, которые, как мне кажется, должны улавливать все конструкторы, но это не работает для массивов (любого типа):

public class InstanceCounter {
  public static void load() {
    ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(Collections.singletonMap("foo.bar.BootstrapState", readBytes(InstanceCounter.class.getClassLoader().getResource("foo/bar/BootstrapState.class"))));
    new AgentBuilder.Default()
      .ignore(none())
      .disableClassFormatChanges()
      .with(RedefinitionStrategy.RETRANSFORMATION)
      .with(InitializationStrategy.NoOp.INSTANCE)
      .with(TypeStrategy.Default.REDEFINE)
    .type(any())
      .transform(new Transformer() {
        @Override
        public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(Advice.to(InstanceCounter.class).on(any()));
        }})
    .installOn(ByteBuddyAgent.install());
  }

  @Advice.OnMethodEnter
  public static void enter(final @Advice.Origin Class<?> origin) {
    if (!BootstrapState.trace || BootstrapState.lock)
      return;

    BootstrapState.lock = true;
    System.out.println(">>> " + origin);
    BootstrapState.lock = false;
  }
}

public class InstanceCounterTest {
  @Test
  public void test() {
    InstanceCounter.load();
    System.out.println("Creating array...");
    BootstrapState.trace = true;
    final Object[] y = new Object[3];
    BootstrapState.trace = false;
    System.out.println("Printing array: " + y);
  }
}

Когда я запустите этот тест, я получу следующие результаты:

Creating array...
Printing array: [Ljava.lang.Object;@7f5eae0f

Если я изменю final Object[] y = new Object[3] на new Object(), то результат будет:

Creating array...
>>> class java.lang.Object
Printing array: [Ljava.lang.Object;@68562ad5

Я дал совет инструментарий как можно более открытый (по крайней мере, настолько открытый, насколько я думаю, я могу его сделать), но это все еще не инструментальное построение объектов массива.

Возможно ли даже инструментальное построение объектов массива?

Обновление 2020-07-28

В соответствии с контекстом исходного вопроса, предоставленные ответы express, что ByteBuddy не имеет прямой поддержки для автоматизации c аппаратура new int[]. Тем не менее, для тех, кто заинтересован в альтернативном решении, я смог достичь такого рода инструментов с помощью Byteman .

Ответы [ 3 ]

1 голос
/ 11 июля 2020

Инструментарий построения обычных объектов включает инъекцию вызова в конструкторы для соответствующего класса. Это возможно, потому что каждый класс объявляет по крайней мере один конструктор, и потому что конструктор всегда вызывается во время создания объекта.

Напротив, массивы не имеют конструкторов, поэтому нет места для внедрения вызова. Это объясняет, почему ваши инструменты для всех конструкторов не собирают создание массивов. вызов приборов.

1 голос
/ 13 июля 2020

Как уже указывалось, вы не можете изменять классы массивов, поскольку они не представлены в файле класса. Вместо этого вам нужно будет перехватить их создание, что потребует синтаксического анализа байтового кода.

Byte Buddy предоставляет ASM, который он сам использует для выполнения такой работы, но это гораздо больше ручная работа, чем перехват конструктора.

1 голос
/ 11 июля 2020

У массивов нет конструкторов; они создаются с помощью newarray (для примитивов) или anewarray (для ссылочных типов) (а также multianewarray, если вы хотите проявить творческий подход).

Если вы хотите изменить создание массива, вам нужно будет найти способ инструментировать эти инструкции байт-кода.

...