Инстанцировать не инстанциируемое
Идея этого топика родилась из этого комментария.Итак имеем: класс без конструкторов, необходимо создать экземпляр этого класса. На Java такого сделать нельзя, так что придется напрямую манипулировать байткодом. Идея состоит в том, чтобы вызвать NEW, но при этом не вызывать
Я взял ASM и начал эксперименты.
Попытка сделать «в лоб«Для начала попытаемся реализовать идею напрямую, генерируем код метода который должен инстанцировать объект без конструктора:
MethodVisitor methodVisitor = classVisitor.visitMethod (ACC_PUBLIC | ACC_STATIC,
FACTORY_METHOD_NAME,
Type.getMethodDescriptor (Type.getObjectType (classToInstantiateInternalName)),
null,
null);
methodVisitor.visitCode ();
methodVisitor.visitTypeInsn (NEW, classToInstantiateInternalName);
if (invokeObjectConstructor) {
methodVisitor.visitInsn (DUP);
methodVisitor.visitMethodInsn (INVOKESPECIAL, Type.getInternalName (Object.class),»
methodVisitor.visitMaxs (1, 0); methodVisitor.visitEnd (); Есть два варианта генерации, вообще без вызова или с вызовом от другого класса (в данном случае Object). Как и ожидалось это не сработало, в обоих случаях получаем VerifyError, в первом случае с сообщением «Expecting to find object/array on stack» (тот самый type uninitialized), во втором «Call to wrong initialization method».Где-то я это уже видел Если почитать описание того как работает сериализация, то там сказано что конструктор класса не вызывается, вызывается только конструктор по умолчанию первого суперкласса не реализующего Serializable. Это именно то что нам нужно.
Если покопаться в исходниках ObjectStreamClass, то можно найти магию в виде sun.reflect.ReflectionFactory.newConstructorForSerialization (). Попытаемся ее использовать:
Constructor
Байткод
public class sun.reflect.GeneratedSerializationConstructorAccessor1 extends sun.reflect.SerializationConstructorAccessorImpl {
public sun.reflect.GeneratedSerializationConstructorAccessor1();
Code:
0: aload_0
1: invokespecial #36 // Method sun/reflect/SerializationConstructorAccessorImpl.»
In all other respects, the bytecode-based reflection framework can be reused for this purpose. This marker class was originally known to the VM and verification disabled for it and all subclasses, but the bug fix for 4486457 necessitated disabling verification for all of the dynamically-generated bytecodes associated with reflection. This class has been left in place to make future debugging easier.
Вот он, источник сильного колдунства ReflectionFactory.Теперь дело за малым, наследуемся от этого класса (кстати баг 4486457, рекомендует наследоваться от MagicAccessorImpl) и пробуем провернуть то же самое. И все работает, причем работают оба варианта как с вызовом
Весь код доступен на Bitbucket. В проекте есть тест который показывает все описанные попытки инстанцировать объект.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.