OPENJPA-2911 addNewObjectIdInstanceMethod in ASM

This commit is contained in:
Mark Struberg 2023-06-26 11:37:49 +02:00
parent 5d63179b48
commit 4b6a38ec1b
2 changed files with 95 additions and 270 deletions

View File

@ -1365,7 +1365,6 @@ public class PCEnhancer {
addCopyFieldsMethod(pc.getClassNode()); addCopyFieldsMethod(pc.getClassNode());
if (_meta.getPCSuperclass() == null || getCreateSubclass()) { if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
addStockMethods(); addStockMethods();
addGetVersionMethod(); addGetVersionMethod();
@ -1389,7 +1388,6 @@ public class PCEnhancer {
addCopyKeyFieldsFromObjectIdMethod(true); addCopyKeyFieldsFromObjectIdMethod(true);
addCopyKeyFieldsFromObjectIdMethod(false); addCopyKeyFieldsFromObjectIdMethod(false);
if (_meta.hasAbstractPKField()) { if (_meta.hasAbstractPKField()) {
addGetIDOwningClass(); addGetIDOwningClass();
} }
@ -1398,10 +1396,9 @@ public class PCEnhancer {
_log.warn(_loc.get("ID-field-in-embeddable-unsupported", _meta.toString())); _log.warn(_loc.get("ID-field-in-embeddable-unsupported", _meta.toString()));
} }
AsmHelper.readIntoBCClass(pc, _pc);
addNewObjectIdInstanceMethod(true); addNewObjectIdInstanceMethod(true);
addNewObjectIdInstanceMethod(false); addNewObjectIdInstanceMethod(false);
AsmHelper.readIntoBCClass(pc, _pc);
} }
else if (_meta.hasPKFieldsFromAbstractClass()) { else if (_meta.hasPKFieldsFromAbstractClass()) {
addGetIDOwningClass(); addGetIDOwningClass();
@ -2868,221 +2865,6 @@ public class PCEnhancer {
instructions.add(lblGo2End); instructions.add(lblGo2End);
} }
/**
* Add code to extract the id of the given primary key relation field for
* setting into an objectid instance.
*/
@Deprecated
private void addExtractObjectIdFieldValueCode(Code code, FieldMetaData pk) {
// if (val != null)
// val = ((PersistenceCapable) val).pcFetchObjectId();
int pc = code.getNextLocalsIndex();
code.astore().setLocal(pc);
code.aload().setLocal(pc);
JumpInstruction ifnull1 = code.ifnull();
code.aload().setLocal(pc);
code.checkcast().setType(PersistenceCapable.class);
if (!pk.getTypeMetaData().isOpenJPAIdentity())
code.invokeinterface().setMethod(PersistenceCapable.class,
PRE + "FetchObjectId", Object.class, null);
else
code.invokeinterface().setMethod(PersistenceCapable.class,
PRE + "NewObjectIdInstance", Object.class, null);
int oid = code.getNextLocalsIndex();
code.astore().setLocal(oid);
code.aload().setLocal(oid);
JumpInstruction ifnull2 = code.ifnull();
// for datastore / single-field identity:
// if (val != null)
// val = ((OpenJPAId) val).getId();
ClassMetaData pkmeta = pk.getDeclaredTypeMetaData();
int pkcode = pk.getObjectIdFieldTypeCode();
Class pktype = pk.getObjectIdFieldType();
if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE
&& pkcode == JavaTypes.LONG) {
code.aload().setLocal(oid);
code.checkcast().setType(Id.class);
code.invokevirtual().setMethod(Id.class, "getId",
long.class, null);
} else if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
code.aload().setLocal(oid);
} else if (pkmeta.isOpenJPAIdentity()) {
switch (pkcode) {
case JavaTypes.BYTE_OBJ:
code.anew().setType(Byte.class);
code.dup();
// no break
case JavaTypes.BYTE:
code.aload().setLocal(oid);
code.checkcast().setType(ByteId.class);
code.invokevirtual().setMethod(ByteId.class, "getId",
byte.class, null);
if (pkcode == JavaTypes.BYTE_OBJ)
code.invokespecial().setMethod(Byte.class, "<init>",
void.class, new Class[] {byte.class});
break;
case JavaTypes.CHAR_OBJ:
code.anew().setType(Character.class);
code.dup();
// no break
case JavaTypes.CHAR:
code.aload().setLocal(oid);
code.checkcast().setType(CharId.class);
code.invokevirtual().setMethod(CharId.class, "getId",
char.class, null);
if (pkcode == JavaTypes.CHAR_OBJ)
code.invokespecial().setMethod(Character.class,
"<init>", void.class, new Class[] {char.class});
break;
case JavaTypes.DOUBLE_OBJ:
code.anew().setType(Double.class);
code.dup();
// no break
case JavaTypes.DOUBLE:
code.aload().setLocal(oid);
code.checkcast().setType(DoubleId.class);
code.invokevirtual().setMethod(DoubleId.class, "getId",
double.class, null);
if (pkcode == JavaTypes.DOUBLE_OBJ)
code.invokespecial().setMethod(Double.class, "<init>",
void.class, new Class[]{double.class});
break;
case JavaTypes.FLOAT_OBJ:
code.anew().setType(Float.class);
code.dup();
// no break
case JavaTypes.FLOAT:
code.aload().setLocal(oid);
code.checkcast().setType(FloatId.class);
code.invokevirtual().setMethod(FloatId.class, "getId",
float.class, null);
if (pkcode == JavaTypes.FLOAT_OBJ)
code.invokespecial().setMethod(Float.class, "<init>",
void.class, new Class[]{float.class});
break;
case JavaTypes.INT_OBJ:
code.anew().setType(Integer.class);
code.dup();
// no break
case JavaTypes.INT:
code.aload().setLocal(oid);
code.checkcast().setType(IntId.class);
code.invokevirtual().setMethod(IntId.class, "getId",
int.class, null);
if (pkcode == JavaTypes.INT_OBJ)
code.invokespecial().setMethod(Integer.class, "<init>",
void.class, new Class[] {int.class});
break;
case JavaTypes.LONG_OBJ:
code.anew().setType(Long.class);
code.dup();
// no break
case JavaTypes.LONG:
code.aload().setLocal(oid);
code.checkcast().setType(LongId.class);
code.invokevirtual().setMethod(LongId.class, "getId",
long.class, null);
if (pkcode == JavaTypes.LONG_OBJ)
code.invokespecial().setMethod(Long.class, "<init>",
void.class, new Class[] {long.class});
break;
case JavaTypes.SHORT_OBJ:
code.anew().setType(Short.class);
code.dup();
// no break
case JavaTypes.SHORT:
code.aload().setLocal(oid);
code.checkcast().setType(ShortId.class);
code.invokevirtual().setMethod(ShortId.class, "getId",
short.class, null);
if (pkcode == JavaTypes.SHORT_OBJ)
code.invokespecial().setMethod(Short.class, "<init>",
void.class, new Class[]{short.class});
break;
case JavaTypes.DATE:
code.aload().setLocal(oid);
code.checkcast().setType(DateId.class);
code.invokevirtual().setMethod(DateId.class, "getId",
Date.class, null);
if (pktype != Date.class) {
// java.sql.Date.class
code.checkcast().setType(pktype);
}
break;
case JavaTypes.STRING:
code.aload().setLocal(oid);
code.checkcast().setType(StringId.class);
code.invokevirtual().setMethod(StringId.class, "getId",
String.class, null);
break;
case JavaTypes.BIGDECIMAL:
code.aload().setLocal(oid);
code.checkcast().setType(BigDecimalId.class);
code.invokevirtual().setMethod(BigDecimalId.class, "getId",
BigDecimal.class, null);
break;
case JavaTypes.BIGINTEGER:
code.aload().setLocal(oid);
code.checkcast().setType(BigIntegerId.class);
code.invokevirtual().setMethod(BigIntegerId.class, "getId",
BigInteger.class, null);
break;
default:
code.aload().setLocal(oid);
code.checkcast().setType(ObjectId.class);
code.invokevirtual().setMethod(ObjectId.class, "getId",
Object.class, null);
}
} else if (pkmeta.getObjectIdType() != null) {
code.aload().setLocal(oid);
if (pkcode == JavaTypes.OBJECT) {
code.checkcast().setType(ObjectId.class);
code.invokevirtual().setMethod(ObjectId.class, "getId",
Object.class, null);
}
code.checkcast().setType(pktype);
} else
code.aload().setLocal(oid);
JumpInstruction go2 = code.go2();
// if (val == null)
// val = <default>;
Instruction def;
switch (pkcode) {
case JavaTypes.BOOLEAN:
def = code.constant().setValue(false);
break;
case JavaTypes.BYTE:
def = code.constant().setValue((byte) 0);
break;
case JavaTypes.CHAR:
def = code.constant().setValue((char) 0);
break;
case JavaTypes.DOUBLE:
def = code.constant().setValue(0D);
break;
case JavaTypes.FLOAT:
def = code.constant().setValue(0F);
break;
case JavaTypes.INT:
def = code.constant().setValue(0);
break;
case JavaTypes.LONG:
def = code.constant().setValue(0L);
break;
case JavaTypes.SHORT:
def = code.constant().setValue((short) 0);
break;
default:
def = code.constant().setNull();
}
ifnull1.setTarget(def);
ifnull2.setTarget(def);
go2.setTarget(code.nop());
}
/** /**
* Adds the <code>pcCopyKeyFieldsFromObjectId</code> methods * Adds the <code>pcCopyKeyFieldsFromObjectId</code> methods
* to classes using application identity. * to classes using application identity.
@ -3409,91 +3191,112 @@ public class PCEnhancer {
* Adds the pcNewObjectIdInstance method to classes using * Adds the pcNewObjectIdInstance method to classes using
* application identity. * application identity.
*/ */
private void addNewObjectIdInstanceMethod(boolean obj) private void addNewObjectIdInstanceMethod(boolean obj) throws NoSuchMethodException {
throws NoSuchMethodException {
// public Object pcNewObjectIdInstance () // public Object pcNewObjectIdInstance ()
Class[] args = (obj) ? new Class[]{Object.class} : null; String mDesc = obj
BCMethod method = _pc.declareMethod(PRE + "NewObjectIdInstance", ? Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
Object.class, args); : Type.getMethodDescriptor(TYPE_OBJECT);
Code code = method.getCode(true);
MethodNode newOidMeth = new MethodNode(Opcodes.ACC_PUBLIC,
PRE + "NewObjectIdInstance",
mDesc,
null, null);
final ClassNode classNode = pc.getClassNode();
classNode.methods.add(newOidMeth);
InsnList instructions = newOidMeth.instructions;
Boolean usesClsString = usesClassStringIdConstructor(); Boolean usesClsString = usesClassStringIdConstructor();
Class oidType = _meta.getObjectIdType(); Class oidType = _meta.getObjectIdType();
if (obj && usesClsString == null) { if (obj && usesClsString == null) {
// throw new IllegalArgumentException (...); // throw new IllegalArgumentException (...);
String msg = _loc.get("str-cons", oidType, String msg = _loc.get("str-cons", oidType, _meta.getDescribedType()).getMessage();
_meta.getDescribedType()).getMessage();
code.anew().setType(IllegalArgumentException.class);
code.dup();
code.constant().setValue(msg);
code.invokespecial().setMethod(IllegalArgumentException.class,
"<init>", void.class, new Class[]{String.class});
code.athrow();
code.calculateMaxStack(); instructions.add(throwException(IllegalArgumentException.class, msg));
code.calculateMaxLocals();
return; return;
} }
if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) { if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
// new ObjectId (cls, oid) // new ObjectId (cls, oid)
code.anew().setType(ObjectId.class); instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ObjectId.class)));
code.dup(); instructions.add(new InsnNode(Opcodes.DUP));
if (_meta.isEmbeddedOnly() || _meta.hasAbstractPKField()) { if (_meta.isEmbeddedOnly() || _meta.hasAbstractPKField()) {
code.aload().setThis(); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
code.invokevirtual().setMethod(PRE + "GetIDOwningClass", instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
Class.class, null); classNode.name,
PRE + "GetIDOwningClass",
Type.getMethodDescriptor(Type.getType(Class.class))));
} }
else { else {
code.classconstant().setClass(getType(_meta)); instructions.add(AsmHelper.getLoadConstantInsn(getType(_meta)));
} }
} }
// new <oid class> (); // new <oid class> ();
code.anew().setType(oidType); instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(oidType)));
code.dup(); instructions.add(new InsnNode(Opcodes.DUP));
if (_meta.isOpenJPAIdentity() || (obj && usesClsString == Boolean.TRUE)) { if (_meta.isOpenJPAIdentity() || (obj && usesClsString == Boolean.TRUE)) {
if ((_meta.isEmbeddedOnly() if ((_meta.isEmbeddedOnly()
&& !(_meta.isEmbeddable() && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION)) && !(_meta.isEmbeddable() && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION))
|| _meta.hasAbstractPKField()) { || _meta.hasAbstractPKField()) {
code.aload().setThis(); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
code.invokevirtual().setMethod(PRE + "GetIDOwningClass", Class.class, null); instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
classNode.name,
PRE + "GetIDOwningClass",
Type.getMethodDescriptor(Type.getType(Class.class))));
} }
else { else {
code.classconstant().setClass(getType(_meta)); instructions.add(AsmHelper.getLoadConstantInsn(getType(_meta)));
} }
} }
String mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE);
if (obj) { if (obj) {
code.aload().setParam(0); instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
code.checkcast().setType(String.class); instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(String.class)));
if (usesClsString == Boolean.TRUE)
args = new Class[]{Class.class, String.class}; if (usesClsString == Boolean.TRUE) {
else if (usesClsString == Boolean.FALSE) mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class), Type.getType(String.class));
args = new Class[]{String.class}; }
else if (usesClsString == Boolean.FALSE) {
mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
}
} }
else if (_meta.isOpenJPAIdentity()) { else if (_meta.isOpenJPAIdentity()) {
// new <type>Identity (XXX.class, <pk>); // new <type>Identity (XXX.class, <pk>);
loadManagedInstance(code, false); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
FieldMetaData pk = _meta.getPrimaryKeyFields()[0]; FieldMetaData pk = _meta.getPrimaryKeyFields()[0];
addGetManagedValueCode(code, pk); addGetManagedValueCode(classNode, instructions, pk, true);
if (pk.getDeclaredTypeCode() == JavaTypes.PC) if (pk.getDeclaredTypeCode() == JavaTypes.PC) {
addExtractObjectIdFieldValueCode(code, pk); int nextFreeVarPos = 1;
if (_meta.getObjectIdType() == ObjectId.class) addExtractObjectIdFieldValueCode(classNode, instructions, pk, nextFreeVarPos);
args = new Class[]{Class.class, Object.class};
else if (_meta.getObjectIdType() == Date.class)
args = new Class[]{Class.class, Date.class};
else
args = new Class[]{Class.class, pk.getObjectIdFieldType()};
} }
code.invokespecial().setMethod(oidType, "<init>", void.class, args); if (_meta.getObjectIdType() == ObjectId.class) {
if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class), Type.getType(Object.class));
code.invokespecial().setMethod(ObjectId.class, "<init>", }
void.class, new Class[]{Class.class, Object.class}); else if (_meta.getObjectIdType() == Date.class) {
code.areturn(); mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class), Type.getType(Date.class));
}
else {
mDescInit = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class), Type.getType(pk.getObjectIdFieldType()));
}
}
code.calculateMaxStack(); instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
code.calculateMaxLocals(); Type.getInternalName(oidType),
"<init>",
mDescInit));
if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
Type.getInternalName(ObjectId.class),
"<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class), Type.getType(Object.class))));
}
instructions.add(new InsnNode(Opcodes.ARETURN));
} }
/** /**
@ -3579,13 +3382,27 @@ public class PCEnhancer {
* exception type, sans message. * exception type, sans message.
*/ */
private InsnList throwException(Class type) { private InsnList throwException(Class type) {
return throwException(type, null);
}
/**
* Helper method to add the code necessary to throw the given
* exception type, sans message.
*/
private InsnList throwException(Class type, String msg) {
InsnList instructions = new InsnList(); InsnList instructions = new InsnList();
instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(type))); instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(type)));
instructions.add(new InsnNode(Opcodes.DUP)); instructions.add(new InsnNode(Opcodes.DUP));
if (msg != null) {
instructions.add(AsmHelper.getLoadConstantInsn(msg));
}
String desc = msg != null
? Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class))
: Type.getMethodDescriptor(Type.VOID_TYPE);
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
Type.getInternalName(type), Type.getInternalName(type),
"<init>", "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE))); desc));
instructions.add(new InsnNode(Opcodes.ATHROW)); instructions.add(new InsnNode(Opcodes.ATHROW));
return instructions; return instructions;

View File

@ -119,6 +119,10 @@ public final class AsmHelper {
ClassNode classNode = new ClassNode(Opcodes.ASM9); ClassNode classNode = new ClassNode(Opcodes.ASM9);
cr.accept(classNode, 0); cr.accept(classNode, 0);
if ((classNode.version & 0xffff) < 49) {
classNode.version = 49;
}
return new ClassNodeTracker(classNode, bcClass.getClassLoader()); return new ClassNodeTracker(classNode, bcClass.getClassLoader());
} }
@ -129,6 +133,10 @@ public final class AsmHelper {
public static void readIntoBCClass(ClassNodeTracker cnt, BCClass bcClass) { public static void readIntoBCClass(ClassNodeTracker cnt, BCClass bcClass) {
// sadly package scoped // sadly package scoped
try { try {
if (bcClass.getMajorVersion() < 49) {
bcClass.setMajorVersion(49);
}
Method readMethod = BCClass.class.getDeclaredMethod("read", InputStream.class, ClassLoader.class); Method readMethod = BCClass.class.getDeclaredMethod("read", InputStream.class, ClassLoader.class);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);