這個答案將展示如何使用ASM的訪問者API來完成(見的ASM homepage的ASM 4.0一個Java字節碼工程庫的第2.2節),因爲它是我最熟悉的API。 ASM也有一個對象模型API(參見同一文檔中的第II部分)變體,在這種情況下可能通常更容易使用。由於它構造了整個類文件在內存中的一棵樹,因此對象模型有點慢,但如果只有少量類需要轉換,性能命中應該可以忽略不計。
當創建static final
字段的值不是常量(如數字)時,它們的初始化實際上會轉到「static initializer block」。因此,你的第二個(轉化)代碼等同於Java代碼:
public class Example {
public static final Example FIRST;
public static final Example SECOND;
static {
FIRST = new Example(1);
SECOND = new Example(2);
}
...
}
在Java文件你被允許有多個這樣的靜態{...}塊,而在課堂上有隻可以將文件是一個。 java編譯器自動將多個靜態塊合併爲一個以滿足此要求。當操縱字節碼時,這意味着如果以前沒有靜態塊,那麼我們創建一個新的靜態塊,如果已經存在一個靜態塊,我們需要將我們的代碼添加到現有代碼的開頭(預先計劃比添加更容易)。
使用ASM,靜態塊看起來像一個靜態方法,其名稱與特殊名稱<clinit>
一樣,就像構造函數看起來像特殊名稱<init>
的方法一樣。
使用visitor api時,知道方法是否從前定義過的方法是偵聽所有visitMethod()調用並檢查每個調用中的方法名稱。在所有的方法被訪問後,visitEnd()方法被調用,所以如果到那時還沒有訪問過方法,我們知道我們需要創建一個新的方法。
假設我們有一個字節[]格式的一部開拓創新類,所請求轉換可以做這樣的:
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
public static byte[] transform(byte[] origClassData) throws Exception {
ClassReader cr = new ClassReader(origClassData);
final ClassWriter cw = new ClassWriter(cr, Opcodes.ASM4);
// add the static final fields
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "FIRST", "LExample;", null, null).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "SECOND", "LExample;", null, null).visitEnd();
// wrap the ClassWriter with a ClassVisitor that adds the static block to
// initialize the above fields
ClassVisitor cv = new ClassVisitor(ASM4, cw) {
boolean visitedStaticBlock = false;
class StaticBlockMethodVisitor extends MethodVisitor {
StaticBlockMethodVisitor(MethodVisitor mv) {
super(ASM4, mv);
}
public void visitCode() {
super.visitCode();
// here we do what the static block in the java code
// above does i.e. initialize the FIRST and SECOND
// fields
// create first instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_1); // pass argument 1 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
// store it in the field
super.visitFieldInsn(PUTSTATIC, "Example", "FIRST", "LExample;");
// create second instance
super.visitTypeInsn(NEW, "Example");
super.visitInsn(DUP);
super.visitInsn(ICONST_2); // pass argument 2 to constructor
super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V");
super.visitFieldInsn(PUTSTATIC, "Example", "SECOND", "LExample;");
// NOTE: remember not to put a RETURN instruction
// here, since execution should continue
}
public void visitMaxs(int maxStack, int maxLocals) {
// The values 3 and 0 come from the fact that our instance
// creation uses 3 stack slots to construct the instances
// above and 0 local variables.
final int ourMaxStack = 3;
final int ourMaxLocals = 0;
// now, instead of just passing original or our own
// visitMaxs numbers to super, we instead calculate
// the maximum values for both.
super.visitMaxs(Math.max(ourMaxStack, maxStack), Math.max(ourMaxLocals, maxLocals));
}
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (cv == null) {
return null;
}
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("<clinit>".equals(name) && !visitedStaticBlock) {
visitedStaticBlock = true;
return new StaticBlockMethodVisitor(mv);
} else {
return mv;
}
}
public void visitEnd() {
// All methods visited. If static block was not
// encountered, add a new one.
if (!visitedStaticBlock) {
// Create an empty static block and let our method
// visitor modify it the same way it modifies an
// existing static block
MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv = new StaticBlockMethodVisitor(mv);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
super.visitEnd();
}
};
// feed the original class to the wrapped ClassVisitor
cr.accept(cv, 0);
// produce the modified class
byte[] newClassData = cw.toByteArray();
return newClassData;
}
因爲你的問題沒有給出確切你的最終目標是什麼進一步的指示,我決定使用硬編碼的基本示例來爲您的示例類案例工作。如果您想要創建正在轉換的類的實例,則必須將包含上述「示例」的所有字符串更改爲使用實際正在轉換的類的完整類名稱。或者,如果您特別需要每個轉換類中的Example類的兩個實例,則上述示例按原樣運行。
這是Java的嗎?這個問題與manen-assembly-plugin有關嗎?然後標記它。 –