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

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

На более высоком уровне моя проблема заключается в том, что если я захочу изменить класс A, который простирается от B, мне также необходимо изменить B. (Учитывая мое понимание того, как классы загружаются в JVM, я считаю, что класс B всегда будет передаваться трансформатору перед классом A. (Пожалуйста, исправьте меня, если я ошибаюсь). Учитывая это предположение, я думаю, что мне тогда нужно вернуться и повторно преобразовать 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** и узнаю, что суперкласс - это 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);
}

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

1 ответ

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

licensed under cc by-sa 3.0 with attribution.