diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
index 7ea61e1c0..dee730d1c 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
@@ -31,6 +31,7 @@ import java.io.ObjectStreamClass;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.io.Serializable;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -3592,19 +3593,11 @@ public class PCEnhancer {
Type.LONG_TYPE.getDescriptor(),
null, uid);
pc.getClassNode().fields.add(serVersField);
-
- /* should be done by ASM FieldNode already
- Code code = getOrCreateClassInitCode(false);
- code.beforeFirst();
- code.constant().setValue(uid.longValue());
- code.putstatic().setField(field);
- */
}
}
- MethodNode writeObjectMeth = AsmHelper.getMethodNode(pc.getClassNode(), "writeObject",
- void.class, ObjectOutputStream.class)
- .orElse(null);
+ MethodNode writeObjectMeth = AsmHelper.getMethodNode(pc.getClassNode(), "writeObject",void.class, ObjectOutputStream.class)
+ .orElse(null);
boolean full = writeObjectMeth == null;
@@ -4320,32 +4313,6 @@ public class PCEnhancer {
return false;
}
- private boolean setVisibilityToSuperMethod(BCMethod method) {
- BCMethod[] methods = _managedType.getMethods(method.getName(),
- method.getParamTypes());
- if (methods.length == 0)
- throw new UserException(_loc.get("no-accessor",
- _managedType.getName(), method.getName()));
- BCMethod superMeth = methods[0];
- if (superMeth.isPrivate()) {
- method.makePrivate();
- return true;
- }
- else if (superMeth.isPackage()) {
- method.makePackage();
- return true;
- }
- else if (superMeth.isProtected()) {
- method.makeProtected();
- return true;
- }
- else if (superMeth.isPublic()) {
- method.makePublic();
- return true;
- }
- return false;
- }
-
/**
* Adds a non-static getter that delegates to the super methods, and
* performs any necessary field tracking.
@@ -5363,22 +5330,6 @@ public class PCEnhancer {
}
}
-
- /**
- * Add the {@link Instruction}s to load the instance to modify onto the
- * stack, and return it. If forStatic
is set, then
- * code
is in an accessor method or another static method;
- * otherwise, it is in one of the PC-specified methods.
- *
- * @return the first instruction added to code
.
- */
- @Deprecated
- private Instruction loadManagedInstance(Code code, boolean forStatic, FieldMetaData fmd) {
- if (forStatic && isFieldAccess(fmd))
- return code.aload().setParam(0);
- return code.aload().setThis();
- }
-
/**
* Add the {@link Instruction}s to load the instance to modify onto the
* stack, and return it. If forStatic
is set, then
@@ -5395,18 +5346,6 @@ public class PCEnhancer {
return new VarInsnNode(Opcodes.ALOAD, 0); // this
}
- /**
- * Add the {@link Instruction}s to load the instance to modify onto the
- * stack, and return it. This method should not be used to load static
- * fields.
- *
- * @return the first instruction added to code
.
- */
- @Deprecated
- private Instruction loadManagedInstance(Code code, boolean forStatic) {
- return loadManagedInstance(code, forStatic, null);
- }
-
private int getAccessorParameterOffset(FieldMetaData fmd) {
return isFieldAccess(fmd) ? 1 : 0;
}
@@ -5840,67 +5779,78 @@ public class PCEnhancer {
* a matching constructor for the provided pk fields. If a match is found, it returns
* the order (relative to the field metadata) of the constructor parameters. If a match
* is not found, returns null.
+ *
+ * We use byte code analysis to find the fields the ct works on.
*/
- private int[] getIdClassConstructorParmOrder(Class> oidType, ArrayList pkfields,
- FieldMetaData[] fmds) {
- Project project = new Project();
- BCClass bc = project.loadClass(oidType);
- BCMethod[] methods = bc.getDeclaredMethods("");
- if (methods == null || methods.length == 0) {
+ private int[] getIdClassConstructorParmOrder(Class> oidType, List pkfields, FieldMetaData[] fmds) {
+ final ClassNode classNode = AsmHelper.readClassNode(oidType);
+ final List cts = classNode.methods.stream()
+ .filter(m -> "".equals(m.name))
+ .collect(Collectors.toList());
+
+ if (cts.isEmpty()) {
return null;
}
int parmOrder[] = new int[pkfields.size()];
- for (BCMethod method : methods) {
- // constructor must be public
- if (!method.isPublic()) {
+ for (MethodNode ct : cts) {
+ if ((ct.access & Opcodes.ACC_PUBLIC) == 0) {
+ // ignore non public constructors
continue;
}
- Class>[] parmTypes = method.getParamTypes();
+ Type[] argTypes = Type.getArgumentTypes(ct.desc);
+
// make sure the constructors have the same # of parms as
// the number of pk fields
- if (parmTypes.length != pkfields.size()) {
+ if (listSize(pkfields) != argTypes.length) {
continue;
}
int parmOrderIndex = 0;
- Code code = method.getCode(false);
- Instruction[] ins = code.getInstructions();
- for (int i = 0; i < ins.length; i++) {
- if (ins[i] instanceof PutFieldInstruction) {
- PutFieldInstruction pfi = (PutFieldInstruction)ins[i];
- for (int j = 0; j < pkfields.size(); j++) {
- int fieldNum = pkfields.get(j);
- // Compare the field being set with the current pk field
- String parmName = fmds[fieldNum].getName();
- Class> parmType = fmds[fieldNum].getType();
- if (parmName.equals(pfi.getFieldName())) {
- // backup and examine the load instruction parm
- if (i > 0 && ins[i-1] instanceof LoadInstruction) {
- LoadInstruction li = (LoadInstruction)ins[i-1];
- // Get the local index from the instruction. This will be the index
- // of the constructor parameter. must be less than or equal to the
- // max parm index to prevent from picking up locals that could have
- // been produced within the constructor. Also make sure the parm type
- // matches the fmd type
- int parm = li.getLocal();
- if (parm <= pkfields.size() && parmTypes[parm-1].equals(parmType)) {
- parmOrder[parmOrderIndex] = fieldNum;
- parmOrderIndex++;
- }
- } else {
- // Some other instruction found. can't make a determination of which local/parm
- // is being used on the putfield.
- break;
+ AbstractInsnNode insn = ct.instructions.getFirst();
+ // skip to the next PUTFIELD instruction
+ while ((insn = searchNextInstruction(insn, i -> i.getOpcode() == Opcodes.PUTFIELD)) != null) {
+ FieldInsnNode putField = (FieldInsnNode) insn;
+ for (int i = 0; i < pkfields.size(); i++) {
+ int fieldNum = pkfields.get(i);
+ // Compare the field being set with the current pk field
+ String parmName = fmds[fieldNum].getName();
+ Class> parmType = fmds[fieldNum].getType();
+ if (parmName.equals(putField.name)) {
+ // backup and examine the load instruction parm
+ if (AsmHelper.isLoadInsn(insn.getPrevious())) {
+ // Get the local index from the instruction. This will be the index
+ // of the constructor parameter. must be less than or equal to the
+ // max parm index to prevent from picking up locals that could have
+ // been produced within the constructor. Also make sure the parm type
+ // matches the fmd type
+
+ VarInsnNode loadInsn = (VarInsnNode) insn.getPrevious();
+
+ int parm = AsmHelper.getParamIndex(ct, loadInsn.var);
+ if (parm < pkfields.size() && argTypes[parm].equals(Type.getType(parmType))) {
+ parmOrder[parmOrderIndex] = fieldNum;
+ parmOrderIndex++;
}
+ } else {
+ // Some other instruction found. can't make a determination of which local/parm
+ // is being used on the putfield.
+ break;
}
}
}
+
+ insn = insn.getNext();
}
if (parmOrderIndex == pkfields.size()) {
return parmOrder;
}
}
+
return null;
}
+
+ private int listSize(Collection> coll) {
+ return coll == null ? 0 : coll.size();
+ }
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
index 6c7f8c919..a5e55ca34 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
@@ -57,15 +57,22 @@ public final class AsmHelper {
/**
* Read the binary bytecode from the class with the given name
- * @param classBytes the binary of the class
+ * @param clazz the class to read into the ClassNode
* @return the ClassNode constructed from that class
*/
- public static ClassNode readClassNode(byte[] classBytes) {
- ClassReader cr = new ClassReader(classBytes);
- ClassNode classNode = new ClassNode();
- cr.accept(classNode, 0);
+ public static ClassNode readClassNode(Class> clazz) {
+ int extPos = clazz.getName().lastIndexOf('.') + 1;
+ String className = clazz.getName().substring(extPos);
+ try (InputStream in = clazz.getResourceAsStream(className + ".class")) {
+ ClassReader cr = new ClassReader(in);
+ ClassNode classNode = new ClassNode();
+ cr.accept(classNode, 0);
- return classNode;
+ return classNode;
+ }
+ catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
}
/**
@@ -432,6 +439,30 @@ public final class AsmHelper {
return types;
}
+ /**
+ * Determine the 0-based index of the parameter of LOAD or STORE position varPos
+ * @param methodNode
+ * @param varPos the position on the stack
+ * @return the index of the parameter which corresponds to this varPos
+ */
+ public static int getParamIndex(MethodNode methodNode, int varPos) {
+ boolean isStatic = (methodNode.access & Opcodes.ACC_STATIC) > 0;
+ if (!isStatic) {
+ // only static methods start with 0
+ // non-static have the this* on pos 0.
+ varPos--;
+ }
+ final Type[] paramTypes = Type.getArgumentTypes(methodNode.desc);
+ int pos = 0;
+ for (int i=0; i