mirror of https://github.com/apache/openjpa.git
OPENJPA-2911 getIdClassConstructorParmOrder in ASM
This commit is contained in:
parent
0ab02d6965
commit
a25ad4fc95
|
@ -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 <code>forStatic</code> is set, then
|
||||
* <code>code</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>code</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 <code>forStatic</code> 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>code</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<Integer> pkfields,
|
||||
FieldMetaData[] fmds) {
|
||||
Project project = new Project();
|
||||
BCClass bc = project.loadClass(oidType);
|
||||
BCMethod[] methods = bc.getDeclaredMethods("<init>");
|
||||
if (methods == null || methods.length == 0) {
|
||||
private int[] getIdClassConstructorParmOrder(Class<?> oidType, List<Integer> pkfields, FieldMetaData[] fmds) {
|
||||
final ClassNode classNode = AsmHelper.readClassNode(oidType);
|
||||
final List<MethodNode> cts = classNode.methods.stream()
|
||||
.filter(m -> "<init>".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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<paramTypes.length; i++) {
|
||||
if (pos==varPos) {
|
||||
return i;
|
||||
}
|
||||
pos += paramTypes[i].getSize();
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot determine parameter position " + varPos + " for method " + methodNode.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the instruction is an LOAD instruction
|
||||
*/
|
||||
|
@ -448,10 +479,9 @@ public final class AsmHelper {
|
|||
|| insn.getOpcode() == Opcodes.BALOAD
|
||||
|| insn.getOpcode() == Opcodes.CALOAD
|
||||
|| insn.getOpcode() == Opcodes.SALOAD;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the instruction is an ALOAD_0
|
||||
*/
|
||||
|
@ -627,4 +657,5 @@ public final class AsmHelper {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.openjpa.enhance;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -26,10 +27,18 @@ import java.io.ByteArrayOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.openjpa.util.QueryException;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.xbean.asm9.tree.ClassNode;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestAsmAdaptor
|
||||
{
|
||||
@Test
|
||||
public void testAsmHelper() throws Exception {
|
||||
final ClassNode classNode = AsmHelper.readClassNode(QueryException.class);
|
||||
assertNotNull(classNode);
|
||||
}
|
||||
@Test
|
||||
public void isEnhanced()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue