Подтвердить что ты не робот

Внедрение Java-метода _before_ вызывается другой метод

Я использую ASM и хочу переписать что-то вроде:

someMethod().targetMethod(args...)

в

someMethod().injectedMethod(arg).targetMethod(args...)

Проблема в том, что я не знаю, что такое метод раньше, я знаю только целевой метод (поэтому поиск someMethod() и инъекция после этого не является вариантом).

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

Используя ASM, я могу легко найти вызов целевого метода, но, к сожалению, стек операндов в этой точке:

[ argN, ..., arg1, instance, ... ]

И пока я могу разобраться, насколько далеко будет присутствовать экземпляр, нет байт-кода, который я могу ввести, который его прочитает. Я знаю, что вы можете сделать это до 4 параметров с помощью трюков с помощью команд dup, но мне нужно общее решение.

Я мог бы добавить кучу локальных переменных и скопировать все из стека, дублировать указанный экземпляр и снова положить все, но что неэффективность во время выполнения я действительно не хочу.

То, что я думаю, будет работать, если бы я мог отслеживать, какая команда отвечала за то, чтобы поместить указатель экземпляра в стек, а затем я мог бы вставлять туда свой метод, а не в вызов целевого метода. Однако мне не повезло найти что-нибудь, чтобы помочь мне сделать это.

Я знаю, что такие вещи, как AspectJ, позволяют это, но должны делать это для множества классов при загрузке, а AspectJ просто слишком медленный.

Может ли кто-нибудь указать мне на инструменты анализа, построенные поверх ASM, которые могут позволить мне сделать это, или кто-нибудь может подумать о более эффективном подходе к инъекции одного вызова метода перед другим?

4b9b3361

Ответ 1

Если я правильно понимаю ваш вопрос, я достиг того же, что и вы, но по-другому.

Используя модификацию байтового кода, управляемого событиями ASM, я в первую очередь переименовал someMethod (arg, arg, arg) в copyOf_someMethod (arg, arg, arg). Затем я создал новый метод, называемый someMethod (arg, arg, arg), который сделал некоторую обработку, а затем вызвал copyOf_someMethod (arg, arg, arg).

Я использовал метод переименования в методе visitMethod (..) реализованного класса ClassVisitor:

MethodVisitor methodVisitor =
    super.visitMethod(
        methodAccess, "copyOf_" + methodName, methodDesc,
            methodSignature, methodExceptions );

return methodVisitor;

В visitMethod (..) я также сохранил все детали подписи метода в переменных класса, готовых для использования в методе visitEnd().

Я фактически сохранил данные метода в объекте MethodDetail и поместил его в очередь:

private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>();

Я создал новую реализацию someMethod (arg, arg, arg), используя метод visitEnd() реализованного ClassVisitor I. Я использовал ASMFier для генерации кода для ввода метода visitEnd(). В реализации использовались данные, которые я хранил ранее в visitMethod (..). Новая реализация сделала некоторую обработку, а затем вызвала copyOf_someMethod(). В методе visitEnd() я вытащил все MethodDetail очереди и для каждого MethodDetail, называемого ASM-кодом, который я ранее создал ASMFier.

Используя этот проект, я создал прокси для метода, который сделал некоторую обработку, а затем вызвал оригинальный метод. Обратите внимание, что исходный метод был переименован в copyOf_someMethod (..). Также обратите внимание, что я представил новую реализацию для исходного метода, который выступал в качестве прокси.

Для поддержки нескольких аргументов я использовал ASMFier для генерации другого кода для 1 arg, 2 arg, 3 arg, e.t.c. Я поддерживал до 7 аргументов и выбрал Unsupported Exception, если у метода, который был проксирован, было более 7 аргументов. В методе visitEnd (..) я назвал другой код (который был сгенерирован ASMFier) в зависимости от того, сколько аргументов метода было использовано исходным методом.

Я использовал javaagent для перехвата загрузки класса и изменения байтов.

Поскольку я новичок в ASM, возможно, я не понял ваш вопрос правильно - однако, если вы задавали вопрос о создании прокси-сервера, который выполняет некоторую обработку, а затем вызывает оригинальный метод, то мое решение работает. Кажется, это было не так медленно. Время загрузки класса для класса, у которого его проксированные методы были не намного медленнее, чем без изменения байтового кода. Скорость выполнения введенного кода не была медленной, ASM-код, созданный ASMFier, кажется очень быстрым.

Приветствия

Ответ 2

В общем случае вы можете выгружать значения из стека во временные локальные переменные. адаптер LocalVariableSorter из пакета CommM Commons делает его очень простым. Но на самом деле методы с более чем 4 аргументами - редкий случай. Во всяком случае, он все еще намного проще и надежнее, чем полный анализ потока данных во время выполнения.

ASM org.objectweb.asm.tree.analysis предоставляет средство для анализа потока данных, например. вы можете использовать SourceInterpreter, чтобы отслеживать, какие команды создавали значения для каждой переменной и слотов стека. Подробнее см. Руководство пользователя ASM.

Ответ 3

Посмотрите на org.objectweb.asm.tree.анализ. SourceIterpreter должен предоставить вам инструкции, которые помещают значение в стек.