2010-12-12 60 views
7

Tôi muốn viết một MethodVisitor biến đổi các lệnh LDC dành cho phép nhân.ASM: Chuyển đổi trạng thái

Ví dụ bytecode:

ldC#26 
imul 

này về cơ bản đẩy một hằng số và sau đó sẽ nhân nó.

Nó phải là một sự chuyển đổi trạng thái vì trước tiên tôi phải kiểm tra xem nó có nhân và, nếu có, tôi cần quay trở lại lệnh ldc và sửa đổi hằng số. Tôi không hoàn toàn chắc chắn làm thế nào tôi sẽ đi về điều này, và tôi không biết làm thế nào để sửa đổi các hằng số (khi tôi đã cố gắng để vượt qua một giá trị khác nhau, giá trị cũ vẫn còn trong hồ bơi liên tục).

Edit:

public class AdditionTransformer extends MethodAdapter { 
    boolean replace = false; 
    int operand = 0; 

    AdditionTransformer(MethodVisitor mv) { 
     super(mv); 
    } 

    @Override 
    public void visitInsn(int opcode) { 
     if (opcode == IMUL && replace) { 
      operand *= 2; 
      visitLdcInsn(operand); 
      replace = false; 
     } 
     mv.visitInsn(opcode); 
    } 

    @Override 
    public void visitLdcInsn(Object cst) { 
     if (cst instanceof Integer && !replace) { 
      operand = (Integer) cst; 
      replace = true; 
     } else { 
      mv.visitLdcInsn(cst); 
     } 
    } 
} 

Đây là những gì tôi có, nhưng nó không loại bỏ các giá trị cũ trong hồ bơi thường xuyên, và nó có thể có lỗi.

Trả lời

1

Nếu bạn quan tâm đến việc sửa đổi bytecode theo cách như vậy, bạn có thể muốn xem xét ASM tree API. Bạn có thể dễ dàng thay thế LdcInsnNode.cst thông qua giao diện cây kiểu DOM thoải mái hơn so với giao diện khách truy cập kiểu SAX mà bạn đang cố gắng sử dụng.

+0

Tôi đã quan tâm tìm kiếm một giải pháp bằng cách sử dụng API khách truy cập, vì ASM đã làm cho nó khá rõ ràng rằng nó được khuyến khích. Tuy nhiên, nếu API cây là lựa chọn tốt hơn trong trường hợp này, tôi sẽ xem xét nó. Cảm ơn. – someguy

+0

Sử dụng API khách truy cập như bạn hiện tại, bạn không thể thay thế hằng số tại chỗ; bạn phải thêm mã bổ sung vào luồng để bật giá trị cũ và đẩy một giá trị mới. Có lẽ bạn nên, tuy nhiên, nhìn vào subclassing ClassWriter; có một vài phương pháp ảo bạn có thể ghi đè thỏa thuận đó bằng cách viết hằng số, mặc dù nó có thể hơi phức tạp để xác minh bạn chỉ đang sửa đổi hằng số bạn dự định. – oldrinb

1

Những gì bạn có là đúng, nhưng không phục vụ cho các loại opcodes khác được gọi sau LDL, vì vậy bạn sẽ gây ra một số vỡ ở đó, vì họ sẽ tìm kiếm thứ gì đó trên ngăn xếp không có ở đó (vì bạn không truy cập vào ldc). Tôi không như vậy chắc chắn về loại bỏ hằng hiện có, nhưng bạn có thể thay thế liên tục như vậy:

@Override 
public void visitInsn(int opcode) { 
    if (opcode == IMUL && replace) { 
     operand *= 2; 
     mv.visitInsn(POP); 
     mv.visitLdcInsn(operand); 
     replace = false; 
    } 
    mv.visitInsn(opcode); 
} 

@Override 
public void visitLdcInsn(Object cst) { 
    if (cst instanceof Integer && !replace) { 
     operand = (Integer) cst; 
     replace = true; 
    } 
    mv.visitLdcInsn(cst); 
}  

Nói cách khác, lúc nào cũng truy cập vào "LDC". Nếu bạn sau đó nhìn thấy một IMUL tiến hành nó, bật ngăn xếp, chèn một hằng số mới, và sau đó truy cập vào các opcode IMUL. Bạn sẽ cần phải làm một chút công việc để thực hiện điều này hoàn toàn an toàn, trong trường hợp một số phương pháp khác được truy cập sau khi truy cập vào ldc và trước IMUL. Để bị hoang tưởng, bạn có thể ghi đè tất cả các phương thức của khách truy cập và nếu nó không phải là visitInsn hoặc không phải IMUL, bạn sẽ truy cập vào ldc và đặt replace = false.

Thay thế hoàn toàn hằng số phức tạp hơn một chút. Bạn cần phải nhớ các hằng số nào đã được nhìn thấy bởi tất cả các phương thức được truy cập trong lớp cho đến giờ. Nếu bạn chưa nhìn thấy hằng số đó, bạn chỉ có thể thay thế giá trị khi bạn truy cập vào ldc.

+0

Cảm ơn, điều này đã giúp một chút, nhưng giải pháp của bạn có vẻ hơi lộn xộn một chút. Nó truyền giá trị cũ và mới, với lệnh POP ở giữa. Bạn có chắc đây là cách duy nhất không? 'Và về việc thay thế hằng số: Tôi nghĩ đó là những gì tôi đang làm, nhưng nó không thực sự loại bỏ giá trị cũ khỏi hồ bơi không đổi. – someguy

+0

@someguy Bạn có thể lưu trữ toán hạng và trì hoãn cuộc gọi visitLdcInsn. Chỉ cần chắc chắn rằng bạn gọi visitLdcInsn nếu visitInsn tiếp theo (opcode)! = IMUL, hoặc phương pháp truy cập tiếp theo! = VisitInsn. Hằng số sẽ chỉ bị xóa nếu không có một đoạn mã nào khác trong lớp tham chiếu đến nó. – axw

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