2010-12-12 2 views
7

곱셈을위한 LDC 명령어를 변환하는 MethodVisitor를 작성하고 싶습니다.ASM : Stateful Transformation

예 바이트 코드 :

ldC#26 
imul 

이것은 기본적으로 상수 푸시 후 곱한다.

우선 상태가 변환되어야합니다. 왜냐하면 먼저 곱셈을하는지 확인해야하고, 그렇다면 ldc 명령으로 돌아가서 상수를 수정해야하기 때문입니다. 나는 이것에 대해 어떻게 생각하는지 완전히 모르겠다. 그리고 상수를 수정하는 법을 모르겠다. (다른 값을 전달하려했을 때, 오래된 값은 여전히 ​​상수 풀에 남아 있었다.)

편집 :

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

이 내가 가지고있는,하지만 상수 풀의 이전 값을 제거하지 않습니다, 그것은 버그가있을 수 있습니다.

답변

1

이와 같은 방식으로 바이트 코드를 수정하려는 경우 ASM tree API을 살펴볼 수 있습니다. LdcInsnNode.cst를 사용하려고하는 SAX 스타일의 방문자 인터페이스와 달리보다 편안한 DOM 스타일의 트리 인터페이스로 쉽게 대체 할 수 있습니다.

+0

ASM이 권장되는 것이 확실하므로 방문자 API를 사용하여 솔루션을 찾고자했습니다. 그러나이 경우 트리 API가 더 나은 선택 인 경우 살펴 보겠습니다. 감사. – someguy

+0

지금처럼 방문자 API를 사용하면 상수 대신 자리를 바꿀 수 없습니다. 흐름에 여분의 코드를 추가하여 이전 값을 팝하고 새 값을 푸시해야합니다. 아마 당신은 ClassWriter의 하위 클래스를 조사해야합니다. 쓰기 상수를 사용하여이 거래를 재정의 할 수있는 몇 가지 가상 메소드가 있지만 의도 한 상수 만 수정한다는 것을 검증하는 것은 다소 복잡 할 수 있습니다. – oldrinb

1

당신이 가지고있는 것은 옳은 것이지만, ldc 이후에 호출되는 다른 유형의 opcode를 고려하지 않으므로 스택에서 무언가를 찾을 것이기 때문에 약간의 파손이 발생할 것입니다 그곳에 있지 않습니다 (당신이 ldc를 방문하지 않았기 때문에). 나는 기존의 상수 제거에 대해 너무 확실하지 않다, 그러나 당신은 일정과 같이 대체 할 수

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

즉, 항상 "LDC"를 참조하십시오. 그런 다음 IMUL이 진행되는 것을 보게되면 스택을 띄우고 새 상수를 삽입 한 다음 IMUL 연산 코드를 방문하십시오. ldc를 방문한 후 IMUL 이전에 다른 방법을 방문 할 경우이 작업을 완전히 안전하게하기 위해 약간의 작업이 필요합니다. 편집증 환자가 되려면 모든 방문자 메소드를 무시하고 IMUL이 아닌 visitInsn이 아닌 경우 ldc를 방문하여 replace = false를 설정합니다.

상수를 완전히 대체하는 것은 약간 까다 롭습니다. 지금까지 클래스에서 방문한 모든 메소드에서 어떤 상수를 보았는지 기억해야합니다. 지금까지 상수를 보지 못했다면, ldc를 방문 할 때 값을 바꿀 수 있습니다.

+0

감사합니다. 약간 도움이되었지만 해결책이 다소 복잡해 보입니다. POP 명령을 사용하여 이전 값과 새 값을 전달합니다. 이것이 유일한 방법이라고 확신합니까? '그리고 상수 대체에 대해 : 나는 그것이 내가하고있는 일이라고 생각했지만 실제로 상수 풀에서 오래된 가치를 제거하는 것이 아닙니다. – someguy

+0

@someguy 피연산자를 저장하고 visitLdcInsn을 지연 할 수 있습니다. 다음 visitInsn (opcode)! = IMUL 또는 다음 방문한 메소드! = visitInsn 인 경우 visitLdcInsn을 호출하십시오. 상수는 참조하는 클래스에 다른 코드가 없으면 제거됩니다. – axw