Я искал это довольно долго, но безуспешно. Есть ли эквивалент в Java ClassFileTransformer
в .NET? По сути, я хочу создать класс CustomClassFileTransformer
(который в Java будет реализовывать интерфейс ClassFileTransformer
), который вызывается при загрузке класса, и ему разрешается настраивать его и заменять его настроенной версией.
Я знаю, что есть фреймворки, которые делают подобные вещи, но я искал что-то более простое, например, реализацию собственной ClassFileTransformer
. Возможно ли это?
РЕДАКТИРОВАТЬ # 1 . Подробнее о том, зачем мне это нужно:
По сути, у меня есть приложение на C #, и мне нужно отслеживать инструкции, которые оно хочет выполнить, чтобы обнаружить операции чтения или записи в поля (операции Ldfld
и Stfld
) и вставьте несколько инструкций до начала чтения / записи.
Я знаю, как это сделать (за исключением части, в которой мне нужно вызвать замену класса): для каждого метода, чей код я хочу отслеживать, я должен:
- Получите метод
MethodBody
, используя MethodBase.GetMethodBody()
- Преобразуйте его в байтовый массив с помощью
MethodBody.GetILAsByteArray()
. Возвращаемое byte[]
содержит байт-код.
- Анализировать байт-код, как объяснено здесь , возможно, вставляя новые инструкции или удаляя / изменяя существующие, изменяя содержимое массива.
- Создайте новый метод и используйте новый байт-код для создания его тела, с
MethodBuilder.CreateMethodBody(byte[] il, int count)
, где il
- массив с байт-кодом.
Я поместил все эти измененные методы в новый класс и использую новый класс, чтобы заменить тот, который первоначально должен был быть загружен.
Альтернативой замене классов было бы как-то получать уведомление всякий раз, когда вызывается метод. Затем я заменил бы вызов этого метода на вызов моего собственного настроенного метода, который я бы настраивал только при первом вызове, а затем помещал бы его в словарь для будущего использования, чтобы уменьшить накладные расходы (для будущих вызовов Я просто посмотрю на метод и вызову его; мне больше не нужно анализировать байт-код). В настоящее время я изучаю способы сделать это, и LinFu выглядит довольно интересно, но если бы было что-то вроде ClassFileTransformer
, это было бы намного проще: я просто переписал класс, заменил его и позволил коду работать без контроля ничего.
Дополнительное примечание: классы могут быть запечатаны. Я хочу иметь возможность заменить любой класс, я не могу наложить ограничения на их атрибуты.
РЕДАКТИРОВАТЬ # 2 . Почему мне нужно сделать это во время выполнения.
Мне нужно отслеживать все, что происходит , чтобы я мог обнаружить каждый доступ к данным. Это относится и к коду библиотечных классов. Однако я не могу заранее знать, какие классы будут использоваться, и даже если бы я знал все возможные классы, которые могут быть загружены , может , было бы огромным ударом по производительности, чтобы настроить все из них, вместо того, чтобы ждать, чтобы увидеть действительно ли они вызваны или нет.
ВОЗМОЖНОЕ (НО ДОВОЛЬНО ХАРДКОР) РЕШЕНИЕ . В случае, если кому-то интересно (и я вижу, что вопрос был задуман, так что, я думаю, кто-то есть), это - это то, на что я сейчас смотрю. По сути, мне нужно было реализовать API профилирования, и я буду регистрироваться на интересующие меня события, в моем случае, когда начинается компиляция JIT. Выдержка из поста блога:
- В обратном вызове ICorProfilerCallback2 :: ModuleLoadFinished вы вызываете ICorProfilerInfo2 :: GetModuleMetadata, чтобы получить указатель на интерфейс метаданных в этом модуле.
- QI для интерфейса метаданных, который вы хотите. Найдите в MSDN «IMetaDataImport» и пролистайте оглавление, чтобы найти темы по интерфейсам метаданных.
- Как только вы окажетесь в стране метаданных, у вас будет доступ ко всем типам в модуле, включая их поля и прототипы функций. Вам может понадобиться проанализировать подписи метаданных, и вам может пригодиться этот анализатор подписей.
- В вашем обратном вызове ICorProfilerCallback2 :: JITCompilationStarted вы можете использовать ICorProfilerInfo2 :: GetILFunctionBody для проверки исходного IL, а ICorProfilerInfo2 :: GetILFunctionBodyAllocator, а затем ICorProfilerInfo2 :: SetILFunctionBody заменить этот IL своим.
Хорошие новости: я получаю уведомление, когда начинается JIT-компиляция, и я могу прямо сейчас заменить байт-код, не беспокоясь о замене класса и т. Д. Не очень хорошие новости: вы не можете вызывать управляемый код из Методы обратного вызова API, что имеет смысл, но означает, что я сам разбираю код IL и т. Д., В отличие от возможности использовать Сесил, что было бы очень просто.
Я не думаю, что есть более простой способ сделать это без использования AOP-фреймворков (например, PostSharp ). Если у кого-то есть другая идея, пожалуйста, дайте мне знать. Я не отмечаю вопрос как ответ.