mirror of
https://github.com/apache/openjpa.git
synced 2025-03-06 16:39:11 +00:00
OPENJPA-2911 addSerialization in ASM
This commit is contained in:
parent
62b14584fb
commit
4d505987c3
@ -603,10 +603,10 @@ public class PCEnhancer {
|
|||||||
addPCMethods();
|
addPCMethods();
|
||||||
addAccessors(pc);
|
addAccessors(pc);
|
||||||
addAttachDetachCode();
|
addAttachDetachCode();
|
||||||
|
addSerializationCode();
|
||||||
|
|
||||||
AsmHelper.readIntoBCClass(pc, _pc);
|
AsmHelper.readIntoBCClass(pc, _pc);
|
||||||
|
|
||||||
addSerializationCode();
|
|
||||||
addCloningCode();
|
addCloningCode();
|
||||||
runAuxiliaryEnhancers();
|
runAuxiliaryEnhancers();
|
||||||
return ENHANCE_PC;
|
return ENHANCE_PC;
|
||||||
@ -3545,19 +3545,18 @@ public class PCEnhancer {
|
|||||||
* as well as creating a custom <code>writeObject</code> method if the
|
* as well as creating a custom <code>writeObject</code> method if the
|
||||||
* class is Serializable and does not define them.
|
* class is Serializable and does not define them.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
private void addSerializationCode() {
|
private void addSerializationCode() {
|
||||||
if (externalizeDetached()
|
if (externalizeDetached() || !Serializable.class.isAssignableFrom(_meta.getDescribedType())) {
|
||||||
|| !Serializable.class.isAssignableFrom(_meta.getDescribedType()))
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getCreateSubclass()) {
|
if (getCreateSubclass()) {
|
||||||
// ##### what should happen if a type is Externalizable? It looks
|
// ##### what should happen if a type is Externalizable? It looks
|
||||||
// ##### like Externalizable classes will not be serialized as PCs
|
// ##### like Externalizable classes will not be serialized as PCs
|
||||||
// ##### based on this logic.
|
// ##### based on this logic.
|
||||||
if (!Externalizable.class.isAssignableFrom(
|
if (!Externalizable.class.isAssignableFrom(_meta.getDescribedType())) {
|
||||||
_meta.getDescribedType()))
|
|
||||||
addSubclassSerializationCode();
|
addSubclassSerializationCode();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3565,105 +3564,121 @@ public class PCEnhancer {
|
|||||||
// is detachable and uses detached state without a declared field,
|
// is detachable and uses detached state without a declared field,
|
||||||
// can't add a serial version UID because we'll be adding extra fields
|
// can't add a serial version UID because we'll be adding extra fields
|
||||||
// to the enhanced version
|
// to the enhanced version
|
||||||
BCField field = _pc.getDeclaredField("serialVersionUID");
|
final Optional<FieldNode> serialVersionUIDNode = pc.getClassNode().fields.stream()
|
||||||
if (field == null) {
|
.filter(f -> f.name.equals("serialVersionUID"))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (serialVersionUIDNode.isEmpty()) {
|
||||||
Long uid = null;
|
Long uid = null;
|
||||||
try {
|
try {
|
||||||
uid = ObjectStreamClass.lookup
|
uid = ObjectStreamClass.lookup(_meta.getDescribedType()).getSerialVersionUID();
|
||||||
(_meta.getDescribedType()).getSerialVersionUID();
|
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
// last-chance catch for bug #283 (which can happen
|
// last-chance catch for bug #283 (which can happen
|
||||||
// in a variety of ClassLoading environments)
|
// in a variety of ClassLoading environments)
|
||||||
if (_log.isTraceEnabled())
|
if (_log.isTraceEnabled()) {
|
||||||
_log.warn(_loc.get("enhance-uid-access", _meta), t);
|
_log.warn(_loc.get("enhance-uid-access", _meta), t);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
_log.warn(_loc.get("enhance-uid-access", _meta));
|
_log.warn(_loc.get("enhance-uid-access", _meta));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if we couldn't access the serialVersionUID, we will have to
|
// if we couldn't access the serialVersionUID, we will have to
|
||||||
// skip the override of that field and not be serialization
|
// skip the override of that field and not be serialization
|
||||||
// compatible with non-enhanced classes
|
// compatible with non-enhanced classes
|
||||||
if (uid != null) {
|
if (uid != null) {
|
||||||
field = _pc.declareField("serialVersionUID", long.class);
|
FieldNode serVersField = new FieldNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
|
||||||
field.makePrivate();
|
"serialVersionUID",
|
||||||
field.setStatic(true);
|
Type.LONG_TYPE.getDescriptor(),
|
||||||
field.setFinal(true);
|
null, uid);
|
||||||
|
pc.getClassNode().fields.add(serVersField);
|
||||||
|
|
||||||
|
/* should be done by ASM FieldNode already
|
||||||
Code code = getOrCreateClassInitCode(false);
|
Code code = getOrCreateClassInitCode(false);
|
||||||
code.beforeFirst();
|
code.beforeFirst();
|
||||||
code.constant().setValue(uid.longValue());
|
code.constant().setValue(uid.longValue());
|
||||||
code.putstatic().setField(field);
|
code.putstatic().setField(field);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code.calculateMaxStack();
|
MethodNode writeObjectMeth = AsmHelper.getMethodNode(pc.getClassNode(), "writeObject",
|
||||||
}
|
void.class, ObjectOutputStream.class)
|
||||||
}
|
.orElse(null);
|
||||||
|
|
||||||
|
boolean full = writeObjectMeth == null;
|
||||||
|
|
||||||
// add write object method
|
// add write object method
|
||||||
BCMethod write = _pc.getDeclaredMethod("writeObject",
|
|
||||||
new Class[]{ObjectOutputStream.class});
|
|
||||||
boolean full = write == null;
|
|
||||||
if (full) {
|
if (full) {
|
||||||
// private void writeObject (ObjectOutputStream out)
|
// private void writeObject (ObjectOutputStream out)
|
||||||
write = _pc.declareMethod("writeObject", void.class,
|
writeObjectMeth = new MethodNode(Opcodes.ACC_PRIVATE,
|
||||||
new Class[]{ObjectOutputStream.class});
|
"writeObject",
|
||||||
write.getExceptions(true).addException(IOException.class);
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(ObjectOutputStream.class)),
|
||||||
write.makePrivate();
|
null,
|
||||||
|
new String[] {Type.getInternalName(IOException.class)});
|
||||||
|
pc.getClassNode().methods.add(writeObjectMeth);
|
||||||
}
|
}
|
||||||
modifyWriteObjectMethod(write, full);
|
modifyWriteObjectMethod(pc.getClassNode(), writeObjectMeth, full);
|
||||||
|
|
||||||
// and read object
|
// and read object
|
||||||
BCMethod read = _pc.getDeclaredMethod("readObject",
|
MethodNode readObjectMeth = AsmHelper.getMethodNode(pc.getClassNode(), "readObject",
|
||||||
new Class[]{ObjectInputStream.class});
|
void.class, ObjectInputStream.class)
|
||||||
full = read == null;
|
.orElse(null);
|
||||||
|
|
||||||
|
full = readObjectMeth == null;
|
||||||
if (full) {
|
if (full) {
|
||||||
// private void readObject (ObjectInputStream in)
|
// private void readObject (ObjectInputStream in)
|
||||||
read = _pc.declareMethod("readObject", void.class,
|
readObjectMeth = new MethodNode(Opcodes.ACC_PRIVATE,
|
||||||
new Class[]{ObjectInputStream.class});
|
"readObject",
|
||||||
read.getExceptions(true).addException(IOException.class);
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(ObjectInputStream.class)),
|
||||||
read.getExceptions(true).addException
|
null,
|
||||||
(ClassNotFoundException.class);
|
new String[] {Type.getInternalName(IOException.class),
|
||||||
read.makePrivate();
|
Type.getInternalName(ClassNotFoundException.class)});
|
||||||
|
pc.getClassNode().methods.add(readObjectMeth);
|
||||||
|
|
||||||
}
|
}
|
||||||
modifyReadObjectMethod(read, full);
|
modifyReadObjectMethod(pc.getClassNode(), readObjectMeth, full);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
private void addSubclassSerializationCode() {
|
private void addSubclassSerializationCode() {
|
||||||
// for generated subclasses, serialization must write an instance of
|
// for generated subclasses, serialization must write an instance of
|
||||||
// the superclass instead of the subclass, so that the client VM can
|
// the superclass instead of the subclass, so that the client VM can
|
||||||
// deserialize successfully.
|
// deserialize successfully.
|
||||||
|
|
||||||
// private Object writeReplace() throws ObjectStreamException
|
// private Object writeReplace() throws ObjectStreamException
|
||||||
BCMethod method = _pc.declareMethod("writeReplace", Object.class, null);
|
MethodNode writeReplaceMeth = new MethodNode(Opcodes.ACC_PRIVATE,
|
||||||
method.getExceptions(true).addException(ObjectStreamException.class);
|
"writeReplace",
|
||||||
Code code = method.getCode(true);
|
Type.getMethodDescriptor(TYPE_OBJECT),
|
||||||
|
null,
|
||||||
|
new String[] {Type.getInternalName(ObjectStreamException.class)});
|
||||||
|
final ClassNode classNode = pc.getClassNode();
|
||||||
|
classNode.methods.add(writeReplaceMeth);
|
||||||
|
InsnList instructions = writeReplaceMeth.instructions;
|
||||||
|
|
||||||
// Object o = new <managed-type>()
|
// Object o = new <managed-type>()
|
||||||
code.anew().setType(_managedType); // for return
|
instructions.add(new TypeInsnNode(Opcodes.NEW, managedType.getClassNode().name));
|
||||||
code.dup(); // for post-<init> work
|
instructions.add(new InsnNode(Opcodes.DUP)); // for post-<init> work
|
||||||
code.dup(); // for <init>
|
instructions.add(new InsnNode(Opcodes.DUP)); // for <init>
|
||||||
code.invokespecial().setMethod(_managedType.getType(), "<init>",
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||||
void.class, null);
|
managedType.getClassNode().name,
|
||||||
|
"<init>",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE)));
|
||||||
|
|
||||||
// copy all the fields.
|
// copy all the fields.
|
||||||
// ##### limiting to JPA @Transient limitations
|
// ##### limiting to JPA @Transient limitations
|
||||||
FieldMetaData[] fmds = _meta.getFields();
|
FieldMetaData[] fmds = _meta.getFields();
|
||||||
for (FieldMetaData fmd : fmds) {
|
for (FieldMetaData fmd : fmds) {
|
||||||
if (fmd.isTransient())
|
if (fmd.isTransient()) {
|
||||||
continue;
|
continue;
|
||||||
// o.<field> = this.<field> (or reflective analog)
|
|
||||||
code.dup(); // for putfield
|
|
||||||
code.aload().setThis(); // for getfield
|
|
||||||
getfield(code, _managedType, fmd.getName());
|
|
||||||
putfield(code, _managedType, fmd.getName(),
|
|
||||||
fmd.getDeclaredType());
|
|
||||||
}
|
}
|
||||||
|
// o.<field> = this.<field> (or reflective analog)
|
||||||
code.areturn().setType(Object.class);
|
instructions.add(new InsnNode(Opcodes.DUP)); // for putfield
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this for getfield
|
||||||
code.calculateMaxLocals();
|
getfield(classNode, instructions, _meta.getDescribedType(), fmd.getName(), fmd.getDeclaredType());
|
||||||
code.calculateMaxStack();
|
putfield(classNode, instructions, _meta.getDescribedType(), fmd.getName(), fmd.getDeclaredType());
|
||||||
|
}
|
||||||
|
instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3682,76 +3697,93 @@ public class PCEnhancer {
|
|||||||
* {@link ObjectOutputStream#defaultWriteObject} method,
|
* {@link ObjectOutputStream#defaultWriteObject} method,
|
||||||
* but only after calling the internal <code>pcSerializing</code> method.
|
* but only after calling the internal <code>pcSerializing</code> method.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
private void modifyWriteObjectMethod(ClassNode classNode, MethodNode method, boolean full) {
|
||||||
private void modifyWriteObjectMethod(BCMethod method, boolean full) {
|
InsnList instructions = new InsnList();
|
||||||
Code code = method.getCode(true);
|
|
||||||
code.beforeFirst();
|
|
||||||
|
|
||||||
// bool clear = pcSerializing ();
|
// bool clear = pcSerializing ();
|
||||||
loadManagedInstance(code, false);
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
code.invokevirtual().setMethod(PRE + "Serializing",
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
boolean.class, null);
|
classNode.name,
|
||||||
int clear = code.getNextLocalsIndex();
|
PRE + "Serializing",
|
||||||
code.istore().setLocal(clear);
|
Type.getMethodDescriptor(Type.BOOLEAN_TYPE)));
|
||||||
|
int clearVarPos = full ? 2 : method.maxLocals+1;
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ISTORE, clearVarPos));
|
||||||
|
|
||||||
if (full) {
|
if (full) {
|
||||||
// out.defaultWriteObject ();
|
// out.defaultWriteObject ();
|
||||||
code.aload().setParam(0);
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||||
code.invokevirtual().setMethod(ObjectOutputStream.class,
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
"defaultWriteObject", void.class, null);
|
Type.getInternalName(ObjectOutputStream.class),
|
||||||
code.vreturn();
|
"defaultWriteObject",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE)));
|
||||||
|
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
|
||||||
|
method.instructions.insert(instructions);
|
||||||
|
instructions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction tmplate = (AccessController.doPrivileged(
|
AbstractInsnNode insn = method.instructions.getFirst();
|
||||||
J2DoPrivHelper.newCodeAction())).vreturn();
|
do {
|
||||||
JumpInstruction toret;
|
// skip to the next RETURN instruction
|
||||||
Instruction ret;
|
while (insn != null && insn.getOpcode() != Opcodes.RETURN) {
|
||||||
code.beforeFirst();
|
insn = insn.getNext();
|
||||||
while (code.searchForward(tmplate)) {
|
|
||||||
ret = code.previous();
|
|
||||||
// if (clear) pcSetDetachedState (null);
|
|
||||||
code.iload().setLocal(clear);
|
|
||||||
toret = code.ifeq();
|
|
||||||
loadManagedInstance(code, false);
|
|
||||||
code.constant().setNull();
|
|
||||||
code.invokevirtual().setMethod(PRE + "SetDetachedState",
|
|
||||||
void.class, new Class[]{Object.class});
|
|
||||||
toret.setTarget(ret);
|
|
||||||
code.next(); // jump over return
|
|
||||||
}
|
}
|
||||||
code.calculateMaxStack();
|
|
||||||
code.calculateMaxLocals();
|
if (insn != null) {
|
||||||
|
InsnList insns = new InsnList();
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ILOAD, clearVarPos));
|
||||||
|
LabelNode lblEndIf = new LabelNode();
|
||||||
|
insns.add(new JumpInsnNode(Opcodes.IFEQ, lblEndIf));
|
||||||
|
insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
|
insns.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||||
|
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
classNode.name,
|
||||||
|
PRE + "SetDetachedState",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class))));
|
||||||
|
insns.add(lblEndIf);
|
||||||
|
method.instructions.insertBefore(insn, insns);
|
||||||
|
|
||||||
|
insn = insn.getNext();
|
||||||
|
}
|
||||||
|
} while (insn != null);
|
||||||
|
|
||||||
|
method.instructions.insert(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a custom readObject method that delegates to the
|
* Adds a custom readObject method that delegates to the
|
||||||
* {@link ObjectInputStream#readObject()} method.
|
* {@link ObjectInputStream#readObject()} method.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
private void modifyReadObjectMethod(ClassNode classNode, MethodNode method, boolean full) {
|
||||||
private void modifyReadObjectMethod(BCMethod method, boolean full) {
|
InsnList instructions = new InsnList();
|
||||||
Code code = method.getCode(true);
|
|
||||||
code.beforeFirst();
|
|
||||||
|
|
||||||
// if this instance uses synthetic detached state, note that it has
|
// if this instance uses synthetic detached state, note that it has
|
||||||
// been deserialized
|
// been deserialized
|
||||||
if (ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())) {
|
if (ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())) {
|
||||||
loadManagedInstance(code, false);
|
|
||||||
code.getstatic().setField(PersistenceCapable.class,
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
"DESERIALIZED", Object.class);
|
instructions.add(new FieldInsnNode(Opcodes.GETSTATIC,
|
||||||
code.invokevirtual().setMethod(PRE + "SetDetachedState",
|
Type.getInternalName(PersistenceCapable.class),
|
||||||
void.class, new Class[]{Object.class});
|
"DESERIALIZED",
|
||||||
|
Type.getDescriptor(Object.class)));
|
||||||
|
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
classNode.name,
|
||||||
|
PRE + "SetDetachedState",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (full) {
|
if (full) {
|
||||||
// in.defaultReadObject ();
|
// in.defaultReadObject ();
|
||||||
code.aload().setParam(0);
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||||
code.invokevirtual().setMethod(ObjectInputStream.class,
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
"defaultReadObject", void.class, null);
|
Type.getInternalName(ObjectInputStream.class),
|
||||||
code.vreturn();
|
"defaultReadObject",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE)));
|
||||||
|
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
}
|
}
|
||||||
|
|
||||||
code.calculateMaxStack();
|
method.instructions.insert(instructions);
|
||||||
code.calculateMaxLocals();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4085,41 +4117,6 @@ public class PCEnhancer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to get the code for the class initializer method,
|
|
||||||
* creating the method if it does not already exist.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
private Code getOrCreateClassInitCode(boolean replaceLast) {
|
|
||||||
BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
|
|
||||||
Code code;
|
|
||||||
if (clinit != null) {
|
|
||||||
code = clinit.getCode(true);
|
|
||||||
if (replaceLast) {
|
|
||||||
Code template = AccessController.doPrivileged(
|
|
||||||
J2DoPrivHelper.newCodeAction());
|
|
||||||
code.searchForward(template.vreturn());
|
|
||||||
code.previous();
|
|
||||||
code.set(template.nop());
|
|
||||||
code.next();
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add static initializer method if non exists
|
|
||||||
clinit = _pc.declareMethod("<clinit>", void.class, null);
|
|
||||||
clinit.makePackage();
|
|
||||||
clinit.setStatic(true);
|
|
||||||
clinit.setFinal(true);
|
|
||||||
|
|
||||||
code = clinit.getCode(true);
|
|
||||||
if (!replaceLast) {
|
|
||||||
code.vreturn();
|
|
||||||
code.previous();
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds bytecode modifying the cloning behavior of the class being
|
* Adds bytecode modifying the cloning behavior of the class being
|
||||||
* enhanced to correctly replace the <code>pcStateManager</code>
|
* enhanced to correctly replace the <code>pcStateManager</code>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user