この答えは(ASM homepageにASM 4.0 A Javaバイトコードエンジニアリングライブラリのセクション2.2を参照)、それはASMの訪問者APIを使用して行うことができる方法を示しています。 ASMには、一般にこの場合に使用しやすいオブジェクト・モデルapi(同じ文書のパートIIを参照)もあります。オブジェクトモデルはメモリ内にクラスファイル全体のツリーを構築するので、少し遅くなるかもしれませんが、変換を必要とするクラスがわずかであれば、パフォーマンスヒットは無視してください。
static final
フィールドの値が定数(数字など)でないフィールドを作成すると、その初期化は実際には "static initializer block"になります。このように、あなたの(変換)は、第2のコードリストは、次のJavaコードと同等です:
public class Example {
public static final Example FIRST;
public static final Example SECOND;
static {
FIRST = new Example(1);
SECOND = new Example(2);
}
...
}
クラスファイルにのみ存在することができますが、あなたは、複数の静的な{...}ブロックを持つことが許可されているjavaファイルで1つです。 Javaコンパイラは、複数の静的ブロックを自動的にこの要件を満たすために1つにマージします。バイトコードを操作する場合、これは前からの静的ブロックが存在しない場合は新しいものを作成し、静的ブロックがすでに存在する場合は既存のものの先頭に追加する必要があることを意味します。
ASMでは、スタティック・ブロックは特別な名前の静的メソッドのように見えます。コンストラクタは特別な名前<init>
のメソッドのようになります。
visitor apiを使用する場合、以前からメソッドが定義されているかどうかを確認するには、すべてのvisitMethod()呼び出しをリッスンし、各呼び出しでメソッド名を確認します。すべてのメソッドが訪問された後でvisitEnd()メソッドが呼び出されるため、それまでにメソッドが訪れていない場合は、新しいメソッドを作成する必要があることがわかります。
我々はバイト[]の形式でのorignalクラスを持っていると仮定すると、要求された変換は次のように行うことができます。
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;
}
あなたの質問は正確にあなたの最終目標が何であるかのさらなる兆候を与えていないので、Iあなたの例のクラスのケースで動作するようにハードコードされた基本的な例を使用することに決めました。変換するクラスのインスタンスを作成する場合は、実際に変換されるクラスの完全なクラス名を使用するには、上記の「例」を含むすべての文字列を変更する必要があります。または、変換されたすべてのクラスでExampleクラスのインスタンスを2つ具体的に使用する場合は、上記の例はそのまま動作します。
これはJavaですか? manen-assembly-pluginに関する質問ですか?その後、それをタグ付けします。 –