in openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java [2170:2452]
private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager) throws NoSuchMethodException {
// public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs, Object oid)
String mDesc = fieldManager
? Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OIDFSTYPE), AsmHelper.TYPE_OBJECT)
: Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT);
MethodNode copyKFMeth = new MethodNode(Opcodes.ACC_PUBLIC,
PRE + "CopyKeyFieldsToObjectId",
mDesc,
null, null);
final ClassNode classNode = pc.getClassNode();
classNode.methods.add(copyKFMeth);
InsnList instructions = copyKFMeth.instructions;
// single field identity always throws exception
if (_meta.isOpenJPAIdentity()) {
instructions.add(AsmHelper.throwException(INTERNEXCEP));
return;
}
// call superclass method
if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st parameter object
if (fieldManager) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // 2nd parameter object
}
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
Type.getInternalName(getType(_meta.getPCSuperclassMetaData())),
PRE + "CopyKeyFieldsToObjectId",
mDesc));
}
// Object id = oid;
if (fieldManager) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // 2nd parameter object
}
else {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st parameter object
}
if (_meta.isObjectIdTypeShared()) {
// oid = ((ObjectId) id).getId ();
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(ObjectId.class),
"getId",
Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
}
// <oid type> id = (<oid type>) oid;
int nextFreeVarPos = (fieldManager) ? 3 : 2;
int idVarPos = nextFreeVarPos++;
Class oidType = _meta.getObjectIdType();
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(oidType)));
instructions.add(new VarInsnNode(Opcodes.ASTORE, idVarPos));
// int inherited = pcInheritedFieldCount;
int inherited = 0;
if (fieldManager) {
instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, INHERIT, Type.INT_TYPE.getDescriptor()));
inherited = nextFreeVarPos++;
instructions.add(new VarInsnNode(Opcodes.ISTORE, inherited));
}
// id.<field> = fs.fetch<type>Field (<index>); or...
// id.<field> = pc.<field>;
FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
: _meta.getDeclaredFields();
// If optimizeIdCopy is enabled and not a field manager method, try to
// optimize the copyTo by using a public constructor instead of reflection
if (_optimizeIdCopy) {
ArrayList<Integer> pkfields = optimizeIdCopy(oidType, fmds);
if (pkfields != null) {
// search for a constructor on the IdClass that can be used
// to construct the IdClass
int[] parmOrder = getIdClassConstructorParmOrder(oidType, pkfields, fmds);
if (parmOrder != null) {
// If using a field manager, values must be loaded into locals so they can be properly ordered
// as constructor parameters.
int[] localIndexes = new int[fmds.length];
if (fieldManager) {
for (int k = 0; k < fmds.length; k++) {
if (!fmds[k].isPrimaryKey()) {
continue;
}
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
instructions.add(AsmHelper.getLoadConstantInsn(k));
instructions.add(new VarInsnNode(Opcodes.ILOAD, inherited));
instructions.add(new InsnNode(Opcodes.IADD));
final Method fieldSupplierMethod = getFieldSupplierMethod(fmds[k].getObjectIdFieldType());
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
Type.getInternalName(fieldSupplierMethod.getDeclaringClass()),
fieldSupplierMethod.getName(),
Type.getMethodDescriptor(fieldSupplierMethod)));
localIndexes[k] = nextFreeVarPos++;
instructions.add(new VarInsnNode(AsmHelper.getStoreInsn(fmds[k].getObjectIdFieldType()), localIndexes[k]));
}
}
// found a matching constructor. parm array is constructor parm order
instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(oidType)));
instructions.add(new InsnNode(Opcodes.DUP));
// build the parm list in order
Class<?>[] clsArgs = new Class<?>[parmOrder.length];
for (int i = 0; i < clsArgs.length; i++) {
int parmIndex = parmOrder[i];
clsArgs[i] = fmds[parmIndex].getObjectIdFieldType();
if (!fieldManager) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
addGetManagedValueCode(classNode, instructions, fmds[parmIndex], true);
}
else {
// Load constructor parameters in appropriate order
instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(fmds[parmIndex].getObjectIdFieldType()), localIndexes[parmIndex]));
if (fmds[parmIndex].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
!fmds[parmIndex].getDeclaredType().isEnum()) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(ObjectId.class),
"getId",
Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
}
// if the type of this field meta data is
// non-primitive and non-string, be sure to cast
// to the appropriate type.
if (!clsArgs[i].isPrimitive() && !clsArgs[i].getName().equals(String.class.getName())) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(clsArgs[i])));
}
}
}
// invoke the public constructor to create a new local id
Type[] parms = Arrays.stream(clsArgs)
.map(Type::getType)
.toArray(Type[]::new);
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
Type.getInternalName(oidType),
"<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, parms)));
int retVarPos = inherited + fmds.length;
instructions.add(new VarInsnNode(Opcodes.ASTORE, retVarPos));
// swap out the app id with the new one
instructions.add(new VarInsnNode(Opcodes.ALOAD, fieldManager ? 2 : 1));
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
instructions.add(new VarInsnNode(Opcodes.ALOAD, retVarPos));
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(ApplicationIds.class),
"setAppId",
Type.getMethodDescriptor(Type.VOID_TYPE,
Type.getType(ObjectId.class), AsmHelper.TYPE_OBJECT)));
instructions.add(new InsnNode(Opcodes.RETURN));
return;
}
}
}
Field field = null;
Method setter = null;
for (int i = 0; i < fmds.length; i++) {
if (!fmds[i].isPrimaryKey()) {
continue;
}
instructions.add(new VarInsnNode(Opcodes.ALOAD, idVarPos));
String name = fmds[i].getName();
Class<?> type = fmds[i].getObjectIdFieldType();
boolean reflect = false;
if (isFieldAccess(fmds[i])) {
field = Reflection.findField(oidType, name, true);
reflect = !Modifier.isPublic(field.getModifiers());
if (reflect) {
instructions.add(AsmHelper.getLoadConstantInsn(oidType));
instructions.add(AsmHelper.getLoadConstantInsn(name));
instructions.add(AsmHelper.getLoadConstantInsn(true));
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(Reflection.class),
"findField",
Type.getMethodDescriptor(Type.getType(Field.class), Type.getType(Class.class),
Type.getType(String.class), Type.BOOLEAN_TYPE)));
}
}
else {
setter = Reflection.findSetter(oidType, name, type, true);
reflect = !Modifier.isPublic(setter.getModifiers());
if (reflect) {
instructions.add(AsmHelper.getLoadConstantInsn(oidType));
instructions.add(AsmHelper.getLoadConstantInsn(name));
instructions.add(AsmHelper.getLoadConstantInsn(type));
instructions.add(AsmHelper.getLoadConstantInsn(true));
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(Reflection.class),
"findSetter",
Type.getMethodDescriptor(Type.getType(Method.class),
Type.getType(Class.class),
Type.getType(String.class),
Type.getType(Class.class),
Type.BOOLEAN_TYPE)));
}
}
if (fieldManager) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
instructions.add(AsmHelper.getLoadConstantInsn(i));
instructions.add(new VarInsnNode(Opcodes.ILOAD, inherited));
instructions.add(new InsnNode(Opcodes.IADD));
final Method fieldSupplierMethod = getFieldSupplierMethod(type);
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
Type.getInternalName(fieldSupplierMethod.getDeclaringClass()),
fieldSupplierMethod.getName(),
Type.getMethodDescriptor(fieldSupplierMethod)));
if (fmds[i].getObjectIdFieldTypeCode() == JavaTypes.OBJECT && !fmds[i].getDeclaredType().isEnum()) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(ObjectId.class),
"getId",
Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
}
// if the type of this field meta data is
// non-primitive and non-string, be sure to cast
// to the appropriate type.
if (!reflect && !type.isPrimitive() && !type.getName().equals(String.class.getName())) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(type)));
}
}
else {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
addGetManagedValueCode(classNode, instructions, fmds[i], true);
// get id/pk from pc instance
if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC) {
addExtractObjectIdFieldValueCode(classNode, instructions, fmds[i], nextFreeVarPos++);
}
}
if (reflect && field != null) {
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(Reflection.class),
"set",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT, Type.getType(Field.class),
(type.isPrimitive()) ? Type.getType(type) : AsmHelper.TYPE_OBJECT)));
}
else if (reflect) {
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(Reflection.class),
"set",
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT, Type.getType(Method.class),
(type.isPrimitive()) ? Type.getType(type) : AsmHelper.TYPE_OBJECT)));
}
else if (field != null) {
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD,
Type.getInternalName(field.getDeclaringClass()),
field.getName(),
Type.getDescriptor(field.getType())));
}
else {
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(setter.getDeclaringClass()),
setter.getName(),
Type.getMethodDescriptor(setter)));
}
}
instructions.add(new InsnNode(Opcodes.RETURN));
}