ASM: преобразование с сохранением состояния - PullRequest
7 голосов
/ 12 декабря 2010

Я хочу написать MethodVisitor, который преобразует инструкции LDC для умножения.

Пример байт-кода:

ldc #26
imul

Это в основном выталкивает константу, а затем умножает ее.

Это должно быть преобразование с учетом состояния, потому что сначала я должен проверить, что оно для умножения, и, если это так, мне нужно вернуться к инструкции ldc и изменить константу. Я не совсем уверен, как мне поступить, и я не знаю, как изменить константу (когда я пытался передать другое значение, старое значение все еще оставалось в пуле констант).

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

Это то, что у меня есть, но оно не удаляет старое значение в пуле констант, и в нем могут быть ошибки.

Ответы [ 2 ]

1 голос
/ 27 января 2011

Если вы заинтересованы в модификации байт-кода таким образом, вы можете обратиться к ASM tree API .Вы можете легко заменить LdcInsnNode.cst через более удобный интерфейс дерева в стиле DOM, в отличие от интерфейса посетителя в стиле SAX, который вы пытаетесь использовать.

1 голос
/ 21 декабря 2010

То, что у вас есть, является почти правильным, но не учитывает другие типы кодов операций, вызываемых после ldc, так что вы вызовете некоторую поломку, так как они будут искать что-то в стеке, чтоне там (так как вы не посетили 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, вы должны посетить ldc и установить replace = false.

Полностью заменить константу немного сложнее.Вам нужно будет вспомнить, какие константы были видны всеми методами, посещенными в классе до сих пор.Если вы еще не видели эту константу, вы можете просто заменить значение при посещении ldc.

...