5

tôi muốn thêm vào lĩnh vực thức tĩnh vào tập tin .class sử dụng ASM, và các tập tin nguồn làLàm thế nào để thêm trường cuối cùng tĩnh với initializer bằng cách sử dụng ASM?

public class Example { 

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

    public int getCode() { 
     return code; 
    } 

    private final int code; 

} 

và lớp tạo được decompiled nên như thế này:

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; 

} 

Và như một kết luận , Tôi muốn thêm hằng số FIRST và SECOND vào tệp .class sử dụng ASM, làm cách nào tôi có thể làm?

+0

Đây có phải là java không? Câu hỏi liên quan đến manen-assembly-plugin? Sau đó gắn thẻ như vậy. –

Trả lời

17

Câu trả lời này cho thấy cách thực hiện bằng cách sử dụng api khách truy cập ASM (xem phần 2.2 của ASM 4.0 Thư viện kỹ thuật bytecode Java trên ASM homepage) vì đó là api quen thuộc nhất đối với tôi. ASM cũng có một mô hình đối tượng api (xem Phần II trong cùng một tài liệu) mà có thể dễ sử dụng hơn trong trường hợp này. Mô hình đối tượng giả sử chậm hơn một chút vì nó xây dựng một cây của toàn bộ tệp lớp trong bộ nhớ, nhưng nếu chỉ có một số lượng nhỏ các lớp cần chuyển đổi hiệu suất nhấn sẽ không đáng kể.

Khi tạo static final trường có giá trị không phải là hằng số (như số), khởi tạo của chúng thực sự đi đến "static initializer block". Do đó, danh sách mã thứ hai (chuyển đổi) của bạn tương đương với mã java sau:

public class Example { 

    public static final Example FIRST; 

    public static final Example SECOND; 

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

    ... 
} 

Trong tệp java bạn được phép có nhiều {...} khối tĩnh như vậy, trong tệp lớp học chỉ có thể trở thành một. Trình biên dịch java tự động kết hợp nhiều khối tĩnh thành một để đáp ứng yêu cầu này. Khi thao tác bytecode điều này có nghĩa là nếu không có khối tĩnh từ trước thì chúng ta tạo một khối mới, trong khi nếu đã tồn tại một khối tĩnh, chúng ta cần thêm mã của chúng ta vào đầu của mã hiện có (việc thêm trước dễ dàng hơn việc thêm).

Với ASM, khối tĩnh trông giống như một phương thức tĩnh với tên đặc biệt <clinit>, giống như các hàm tạo giống như các phương thức có tên đặc biệt <init>.

Khi sử dụng api khách truy cập, cách để biết liệu một phương pháp đã được xác định từ trước đó là lắng nghe tất cả các cuộc gọi visitMethod() và kiểm tra tên phương thức trong mỗi cuộc gọi. Sau khi tất cả các phương thức đã được truy cập, phương thức visitEnd() được gọi, vì vậy nếu không có phương thức nào được truy cập thì chúng ta cần tạo một phương thức mới.

Giả sử chúng ta có một lớp orignal trong byte [] định dạng, chuyển đổi yêu cầu có thể được thực hiện như thế này:

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

Kể từ câu hỏi của bạn đã không đưa ra chỉ dẫn thêm về chính xác những gì mục tiêu cuối cùng của bạn là, tôi đã quyết định đi với một ví dụ cơ bản được mã hóa cứng để làm việc cho trường hợp Lớp mẫu của bạn. Nếu bạn muốn tạo các thể hiện của lớp đang được chuyển đổi, bạn sẽ phải thay đổi tất cả các chuỗi chứa "Example" ở trên để sử dụng tên lớp đầy đủ của lớp thực sự được chuyển đổi thay thế. Hoặc nếu bạn đặc biệt muốn hai cá thể của lớp Example trong mỗi lớp được chuyển đổi, ví dụ trên hoạt động như là.

+2

Ước gì tôi có thể trả lời 10 câu trả lời này. Việc thêm/xóa các phương thức với ASM rất dễ dàng. Câu trả lời này cho thấy kỹ thuật quan trọng để sửa đổi chúng. –

Các vấn đề liên quan