2010-12-12 16 views
7

Chcę napisać MethodVisitor, który przekształca instrukcje LDC, które są dla mnożenia.ASM: Stateful Transformation

Przykład bajtowy:

ldC#26 
imul 

Zasadniczo popycha stały i następnie mnoży się.

Musi to być transformacja stanowa, ponieważ najpierw muszę sprawdzić, czy jest ona dla mnożenia, a jeśli tak jest, muszę wrócić do instrukcji ldc i zmodyfikować stałą. Nie jestem do końca pewny, jak to zrobić, i nie wiem, jak modyfikować stałą (kiedy próbowałem przekazać inną wartość, stara wartość nadal pozostawała w puli stałej).

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

To co mam, ale nie usunąć starą wartość w stałej puli i może zawierać błędy.

Odpowiedz

1

Jeśli jesteś zainteresowany modyfikowaniem kodu bajtowego w taki sposób, możesz zajrzeć do ASM tree API. Możesz łatwo zamienić plik LdcInsnNode.cst na bardziej wygodny interfejs drzewa w stylu DOM, w przeciwieństwie do interfejsu użytkownika w stylu SAX, którego próbujesz użyć.

+0

Bardzo chciałem znaleźć rozwiązanie za pomocą interfejsu API odwiedzającego, ponieważ ASM stwierdziło, że jest to zalecane. Jednakże, jeśli API drzewa jest lepszym wyborem w tym przypadku, zajrzę do niego. Dzięki. – someguy

+0

Korzystając z interfejsu API gościa, jakim jesteś teraz, nie możesz po prostu zastąpić stałej w miejscu; musisz dodać do strumienia dodatkowy kod, aby wypchnąć starą wartość i wcisnąć nową. Być może jednak powinieneś zajrzeć do podklasy ClassWriter; istnieje kilka wirtualnych metod, które można przesłonić, mając do czynienia ze stałą zapisu, chociaż może być trochę skomplikowane, aby sprawdzić, czy modyfikujesz tylko stałą, którą chcesz. – oldrinb

1

To, co masz, jest w porządku, ale nie obsługuje innych typów kodów, które są wywoływane po Ldc, więc spowodujesz tam pewne uszkodzenie, ponieważ będą szukały czegoś na stosie to nie jest (ponieważ nie odwiedziłeś LSD). Nie jestem tak pewny o usunięcie istniejących na stałym poziomie, ale można zastąpić stałą tak:

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

Innymi słowy, zawsze odwiedzić „LDC”. Jeśli zobaczysz postępujący IMUL, wyślij stos, wstaw nową stałą, a następnie odwiedź kod operacyjny IMUL. Trzeba by trochę popracować, aby było to całkowicie bezpieczne, na wypadek, gdyby jakaś inna metoda była odwiedzana po wizycie w ldc i przed IMUL. Aby być paranoikiem, można zastąpić wszystkie metody odwiedzin, a jeśli nie jest to wizyta lub nie, należy odwiedzić ldc i ustawić zamianę = fałsz.

Całkowicie zastępując stałą jest nieco trudniejsze. Musisz pamiętać, które stałe były dotychczas widziane przez wszystkie metody odwiedzone w klasie. Jeśli nie widziałeś dotychczas takiej stałej, możesz po prostu zastąpić wartość, gdy odwiedzasz LSD.

+0

Dzięki, pomogło to trochę, ale twoje rozwiązanie wydaje się trochę nieporządne. Przekazuje starą wartość i nową, z instrukcją POP pomiędzy. Jesteś pewien, że to jedyny sposób? "A co do zamiany stałej: myślałem, że to właśnie robię, ale tak naprawdę nie usuwa ona starej wartości ze stałej puli. – someguy

+0

@someguy Możesz zapisać operand i odroczyć wywołanie visitLdcInsn. Po prostu upewnij się, że zadzwonisz do visitLdcInsn, jeśli następna wizytaInsn (opcode)! = IMUL lub następna odwiedzona metoda! = VisitInsn. Stała zostanie usunięta tylko wtedy, gdy w klasie nie ma innego fragmentu kodu, który się do niej odnosi. – axw