5

Ben ASM .class dosyasına statik nihai bir alan eklemek istiyoruz ve kaynak dosyaASM kullanarak başlatıcı ile statik son alan nasıl eklenir?

public class Example { 

    public Example(int code) { 
     this.code = code; 
    } 

    public int getCode() { 
     return code; 
    } 

    private final int code; 

} 

ve böyle olmalı decompiled oluşturulan sınıftır:

public class Example { 

    public static final Example FIRST = new Example(1); 

    public static final Example SECOND = new Example(2); 

    public Example(int code) { 
     this.code = code; 
    } 

    public int getCode() { 
     return code; 
    } 

    private final int code; 

} 

Ve sonuç olarak , ASM kullanarak .class dosyasına FIRST ve SECOND sabitlerini eklemek istiyorum, nasıl yapabilirim?

+0

Bu java mı? Soru manen-montaj-eklenti ile ilgili mi? Sonra bunu etiketleyin. –

cevap

17

Bu cevap o benim için en tanıdık api çünkü (ASM homepage üzerine ASM 4,0 Bir Java bytecode mühendislik kütüphanesinde bölümünü 2.2 bakınız) ASM ziyaretçi api kullanılarak yapılabilir gösterir. ASM ayrıca, bu durumda kullanılması genellikle daha kolay olabilecek bir nesne modeli API'sine (aynı belgede Bölüm II'ye bakın) sahiptir. Nesne modeli, bellekte tüm sınıf dosyasının bir ağacını oluşturduğundan, ancak biraz daha yavaş bir performans sergilediğinden, performans vuruşunu dönüştürmek için gereken yalnızca küçük bir sınıf varsa, ihmal edilebilir.

Değerleri sabit olmayan (sayı gibi) static final alanları oluştururken, bunların başlatılması aslında bir "static initializer block" olur. sınıfta orada can sadece dosyaları ederken,

public class Example { 

    public static final Example FIRST; 

    public static final Example SECOND; 

    static { 
    FIRST = new Example(1); 
    SECOND = new Example(2); 
    } 

    ... 
} 

bir java dosyasında birden böyle statik {...} blokları için izin verilir: Böylece, ikinci (dönüştürülmüş) kod listesi aşağıdaki java kodu eşdeğerdir tek ol. Java derleyicisi, bu gereksinimi karşılamak için birden çok statik bloğu otomatik olarak birleştirir. Bayt kodu manipüle edildiğinde bu, eğer daha önce hiç bir statik blok yoksa, yeni bir tane yaratırız, eğer mevcut bir statik blok varsa, mevcut kodun başlangıcına kodumuzu eklememiz gerekir (ön hazırlık, ekleme yapmaktan daha kolaydır).

ASM ile statik blok, <clinit> özel adıyla statik bir yönteme benziyor; tıpkı yapıcıların özel adı <init> olan yöntemlere benzemesi gibi.

Ziyaretçi api'sini kullanırken, bir yöntemin daha önce tanımlanıp tanımlanmadığını bilmenin yolu, tüm visitMethod() çağrılarını dinlemektir ve her çağrıda yöntem adını kontrol etmektir. Tüm yöntemler ziyaret edildikten sonra visitEnd() yöntemi çağrılır, bu nedenle o zamana kadar hiçbir yöntem ziyaret edilmediyse, yeni bir yöntem oluşturmamız gerektiğini biliyoruz.

Byteların [] formatında bir orignal sınıf var varsayarsak, istenen dönüşümü böyle yapılabilir:

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; 
} 

soru tam olarak nihai hedef ne olduğu ileri göstergeler vermedi beri, Örnek sınıf durumunuz için çalışmak için kodlanmış temel bir örnekle devam etmeye karar verdik. Dönüştürülmüş sınıfın örneklerini oluşturmak isterseniz, bunun yerine dönüştürülmüş olan sınıfın tam sınıf adını kullanmak için yukarıdaki "Örnek" i içeren tüm dizeleri değiştirmeniz gerekir. Veya her dönüştürülmüş sınıfta Örnek sınıfının iki örneğini özellikle istiyorsanız, yukarıdaki örnek olduğu gibi çalışır.

+2

Bu cevabı 10 tane daha verdim. ASM ile yöntemler eklemek/kaldırmak kolaydır. Bu cevap onları değiştirmek için kritik bir teknik göstermektedir. –

İlgili konular