[英]Editing the finally block of try-catch in Java ASM bytecode
我有一個 java class,我想在每個 finally 塊中添加一些自定義指令。 我試過使用 Java ASM 庫。 我嘗試迭代每個方法,然后迭代每個 TryCatchBlockNode 並獲取句柄 label 並在其中插入指令。
List<MethodNode> methods = (List<MethodNode>) classNode.methods;
for (MethodNode method : methods) {
List<TryCatchBlockNode> tryCatchBlocks = method.tryCatchBlocks;
for (int i = 0; i < tryCatchBlocks.size(); i++) {
TryCatchBlockNode tryCatchBlockNode = (TryCatchBlockNode) method.tryCatchBlocks.get(i);
if (tryCatchBlockNode.type == null) {
InsnList tmpInsn = new InsnList();
tmpInsn.add(new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
tmpInsn.add(new LdcInsnNode("AAAA"));
tmpInsn.add(new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));
method.instructions.insert(tryCatchBlockNode.handler, tmpInsn);
}
}
}
如何在每個 finally 塊中插入一些自定義代碼?
編輯:上面的代碼有效,但重構不符合預期:
輸入代碼是:
try {
lr.abort();
} finally {
System.out.println("throwable");
}
使用 IntelliJ 的反編譯代碼 Output:
try {
lr.abort();
} catch (Throwable var3) {
System.out.println("AAAA");
System.out.println("throwable");
throw var3;
}
System.out.println("throwable");
System.out.println("Goodbye!");
您在 label 節點之后重復插入一個指令節點,而您實際上想要在剛剛插入的節點之后插入指令節點。
一般模式是
以與插入說明相反的順序結束。
有不同的方法可以解決這個問題
只需使用相反的順序
TryCatchBlockNode tryCatchBlockNode = method.tryCatchBlocks.get(i); if(tryCatchBlockNode.type == null) { LabelNode h = tryCatchBlockNode.handler; method.instructions.insert(h, new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V")); method.instructions.insert(h, new LdcInsnNode("AAAA")); method.instructions.insert(h, new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); }
這很簡單,但隨着您必須插入的說明越多,閱讀起來就越困難。
不要使用固定點,而是使用先前插入的節點
TryCatchBlockNode tryCatchBlockNode = method.tryCatchBlocks.get(i); if(tryCatchBlockNode.type == null) { AbstractInsnNode after = tryCatchBlockNode.handler; method.instructions.insert(after, after = new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); method.instructions.insert(after, after = new LdcInsnNode("AAAA")); method.instructions.insert(after, after = new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V")); }
這允許以預期的結果順序指定指令,但需要一個變化的變量。
獲取 label 之后的下一個節點並改用insertBefore
TryCatchBlockNode tryCatchBlockNode = method.tryCatchBlocks.get(i); if(tryCatchBlockNode.type == null) { AbstractInsnNode before = tryCatchBlockNode.handler.getNext(); method.instructions.insertBefore(before, new FieldInsnNode(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")); method.instructions.insertBefore(before, new LdcInsnNode("AAAA")); method.instructions.insertBefore(before, new MethodInsnNode(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V")); }
在這里工作順利,因為異常處理程序不能為空。 所以必須有一個后續節點,我們可以參考。 但它可能不適用於其他場景。
您還可以使用指令列表並將它們作為單個實體插入。
static InsnList printStatement() {
InsnList print = new InsnList();
print.add(new FieldInsnNode(GETSTATIC,
"java/lang/System", "out", "Ljava/io/PrintStream;"));
print.add(new LdcInsnNode("AAAA"));
print.add(new MethodInsnNode(INVOKEVIRTUAL,
"java/io/PrintStream", "println", "(Ljava/lang/String;)V"));
return print;
}
TryCatchBlockNode tryCatchBlockNode = method.tryCatchBlocks.get(i);
if(tryCatchBlockNode.type == null) {
method.instructions.insert(tryCatchBlockNode.handler, printStatement());
}
這樣的構建塊更容易維護。 請務必記住,這會將節點從一個列表轉移到另一個列表,因此每次需要插入序列時都必須再次調用工廠方法,而不是重復使用InsnList
object。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.