Как получить, где объект инициализируется с помощью ASM? - PullRequest
1 голос
/ 11 октября 2019

У меня есть определенный класс, о котором я хочу позаботиться.

public class TargetClass {
   public TargetClass() { /* .. */ }

}

В моем большом проекте у меня есть несколько мест, где я создаю новый экземпляр этого класса

public class A {
   ...
   TargetClass obj = new TargetClass() // say line number 100
   ...
}
public class B {
   ...
   TargetClass obj = new TargetClass() // say line number 200
   ...
}

Как я могу использовать ASM для вставки поля initLocation в конструктор, и когда вызывается new TargetClass(), initLocation запишет номер строки местоположения инициализации, например, package/path/A + L100, package/path/B + L200

1 Ответ

0 голосов
/ 03 ноября 2019

Это очень просто:

        ClassNode node = context.getInfo().getNode(); // You have to make ClassNode by yourself. 
        for (MethodNode method : node.methods) {
            int lineNumber = -1;
            ListIterator<AbstractInsnNode> iter = method.instructions.iterator();
            while (iter.hasNext()) {
                AbstractInsnNode instruction = iter.next();
                if (instruction instanceof MethodInsnNode) {
                    MethodInsnNode invokeInsn = (MethodInsnNode) instruction;
                    if (invokeInsn.getOpcode() == Opcodes.INVOKESPECIAL && invokeInsn.name.equals("<init>")) {
                        if (invokeInsn.owner.equals("Receiver")) { // Here we have to use internal class name. For example java.lang.Class = java/lang/Class
                            iter.previous();
                            iter.add(new InsnNode(Opcodes.DUP));
                            iter.next();
                            iter.add(new LdcInsnNode(node.name + " " + method.name + method.desc + " L" + lineNumber));
                            iter.add(new FieldInsnNode(Opcodes.PUTFIELD, "Receiver", "initializationInfo", "Ljava/lang/String;"));
                        }
                    }
                } else if (instruction instanceof LineNumberNode) {
                    lineNumber = ((LineNumberNode) instruction).line;
                }
            }
        }

Я просто перебираю каждую инструкцию в каждом методе. Когда я нахожу INVOKESPECIAL, я сравниваю целевой метод с <init> (внутреннее имя конструктора) и имя владельца с Receiver. Вы, вероятно, замените его именем вашего собственного класса. Когда все эти условия выполняются, я вставляю инструкцию DUP перед вызовом конструктора и LDC и PUTFIELD после этого.

Тестовый класс

public class Transmitter {

    public static void main(String[] args) {
        Receiver r = new Receiver();

        System.out.println(r.initializationInfo);
    }
}

Результат

java -cp main Transmitter
Transmitter main([Ljava/lang/String;)V L4
...