Как правильно использовать Instrumentation.retransformClasses() из ассемблерного кода?

Я использую библиотеку asm для выполнения некоторой модификации байт-кода Java, в частности, для модификации моих классов для реализации нового интерфейса и связанных методов. Мой текущий подход заключается в использовании основного API asm через javaagent. Я хотел бы сохранить этот динамический подход, а не статически изменять файлы .class.

На более высоком уровне моя проблема заключается в том, что если я решу изменить класс A, который расширяется от B, мне также нужно изменить B. (Учитывая мое понимание того, как классы загружаются в JVM, я считаю, что класс B всегда будет был передан преобразователю перед классом А. (Пожалуйста, поправьте меня, если я ошибаюсь). Учитывая это предположение, я думаю, что мне нужно вернуться и повторно преобразовать Б. Мой подход отражен в этот бит кода:

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) {
      throws IllegalClassFormatException {
    // **1**
    System.out.println("--->>> " + name);

    if (interestingClass(name)) {
        try {
            ClassReader cr = new ClassReader(b);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
            cr.accept(pv, 0);

            // **2** Retrieve the superclass and try to transform that
            if (! "Ljava/lang/Object;".equals(pv.getSuperName())) {
                String cName = classJvmToCanonical(pv.getSuperName());
                Class[] classes = inst.getAllLoadedClasses();
                for (Class c : classes) {
                    if (c.getName().equals(cName)) {
                        inst.retransformClasses(c);
                        break;
                    }
                }
            }

            // Dump the transformed class
            ClassReader cr2 = new ClassReader(cw.toByteArray());
            ClassWriter cw2 = new ClassWriter(cr2, 0);
            TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out));
            cr2.accept(tcv, 0);

            return cw2.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    } else {
        return b;
    }
}

(inst — это дескриптор Instrumentation, который передается в конструкторе)

Часть, с которой у меня проблемы, — это блок, отмеченный в комментариях **2**. Скажем еще раз, что A расширяет B, и я «заинтересован» в преобразовании A. Я ожидаю, что увижу имя суперкласса (B), напечатанное в **1** (но не преобразовываясь, потому что я не думаю, это интересно на первом проходе), а затем, как только я доберусь до **2** и обнаружу, что суперклассом A является B, я должен попытаться повторно преобразовать B. В этот момент я ожидаю, что этот метод будет вызван снова (через inst.retransformClasses()) и что я увижу, как B печатается в **1**. Однако я этого не делаю. (Я добавил операторы печати и уверен, что дошел до вызова повторного преобразования. Я также проверил, что Instrumentation.isRetransformClassesSupported() и Instrumentation.isModifiableClass(c) возвращают true).

Я считаю, что правильно настроил агент; установка для Can-Retransform-Classes и Can-Redefine-Classes значения true в манифесте. Кроме того, когда я добавляю преобразователь в инструментарий в методе premain агента, я делаю это:

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new PyClassFileTransformer(inst), true);
}

Любые идеи относительно того, что я делаю неправильно здесь? Спасибо.


person Jens    schedule 01.03.2012    source источник
comment
У тебя есть решение твоей проблемы? @Jens Можете ли вы пойти здесь, чтобы дать мне несколько советов?   -  person Nick Dong    schedule 09.09.2013


Ответы (1)


Вы можете изменить свою стратегию инструментирования байт-кода, поэтому, когда класс B загружается, вы находите все его подклассы и в этот момент решаете, нужно ли вам модифицировать класс B сейчас. Это можно оптимизировать, поддерживая репозиторий метаданных классов или кеш в памяти (т. е. информацию об иерархии классов), чтобы вам не приходилось каждый раз загружать метаданные.

person Eugene Kuleshov    schedule 02.03.2012