mirror of https://github.com/apache/openjpa.git
OPENJPA-2911 start removing Serp from PCDataGenerator
wip
This commit is contained in:
parent
b04c985956
commit
60ccb26050
|
@ -29,10 +29,13 @@ import org.apache.openjpa.enhance.PCDataGenerator;
|
|||
import org.apache.openjpa.kernel.AbstractPCData;
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreContext;
|
||||
import org.apache.openjpa.lib.util.StringUtil;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.meta.ValueMetaData;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
|
||||
import serp.bytecode.BCClass;
|
||||
import serp.bytecode.BCField;
|
||||
|
@ -40,6 +43,7 @@ import serp.bytecode.BCMethod;
|
|||
import serp.bytecode.Code;
|
||||
import serp.bytecode.Instruction;
|
||||
import serp.bytecode.JumpInstruction;
|
||||
import serp.bytecode.Project;
|
||||
|
||||
/**
|
||||
* A {@link PCDataGenerator} instance which generates properly
|
||||
|
@ -78,12 +82,18 @@ public class DataCachePCDataGenerator extends PCDataGenerator {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void decorate(BCClass bc, ClassMetaData meta) {
|
||||
enhanceToData(bc);
|
||||
enhanceToNestedData(bc);
|
||||
replaceNewEmbeddedPCData(bc);
|
||||
addSynchronization(bc);
|
||||
addTimeout(bc);
|
||||
protected void decorate(ClassNodeTracker cnt, ClassMetaData meta) {
|
||||
//X TODO REMOVE
|
||||
BCClass _bc = new Project().loadClass(cnt.getClassNode().name.replace("/", "."));
|
||||
AsmHelper.readIntoBCClass(cnt, _bc);
|
||||
|
||||
enhanceToData(_bc);
|
||||
enhanceToNestedData(_bc);
|
||||
replaceNewEmbeddedPCData(_bc);
|
||||
addSynchronization(_bc);
|
||||
addTimeout(_bc);
|
||||
|
||||
cnt.setClassNode(AsmHelper.toClassNode(cnt.getProject(), _bc).getClassNode());
|
||||
}
|
||||
|
||||
private void enhanceToData(BCClass bc) {
|
||||
|
@ -185,6 +195,44 @@ public class DataCachePCDataGenerator extends PCDataGenerator {
|
|||
code.calculateMaxStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean field of the given name and type.
|
||||
*/
|
||||
@Deprecated
|
||||
private BCField addBeanField(BCClass bc, String name, Class type) {
|
||||
if (name == null)
|
||||
throw new IllegalArgumentException("name == null");
|
||||
|
||||
// private <type> <name>
|
||||
BCField field = bc.declareField(name, type);
|
||||
field.setAccessFlags(getFieldAccess());
|
||||
name = StringUtil.capitalize(name);
|
||||
|
||||
// getter
|
||||
String prefix = (type == boolean.class) ? "is" : "get";
|
||||
BCMethod method = bc.declareMethod(prefix + name, type, null);
|
||||
method.makePublic();
|
||||
Code code = method.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.getfield().setField(field);
|
||||
code.xreturn().setType(type);
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
|
||||
// setter
|
||||
method = bc.declareMethod("set" + name, void.class,
|
||||
new Class[]{ type });
|
||||
method.makePublic();
|
||||
code = method.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.xload().setParam(0).setType(type);
|
||||
code.putfield().setField(field);
|
||||
code.vreturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
return field;
|
||||
}
|
||||
|
||||
private void addTimeout(BCClass bc) {
|
||||
bc.declareInterface(DataCachePCData.class);
|
||||
bc.declareInterface(Timed.class);
|
||||
|
|
|
@ -19,30 +19,40 @@
|
|||
package org.apache.openjpa.enhance;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.AccessController;
|
||||
|
||||
import org.apache.openjpa.lib.util.J2DoPrivHelper;
|
||||
import org.apache.openjpa.lib.util.StringUtil;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.util.InternalException;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
import org.apache.openjpa.util.asm.EnhancementClassLoader;
|
||||
import org.apache.openjpa.util.asm.EnhancementProject;
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
import org.apache.xbean.asm9.Type;
|
||||
import org.apache.xbean.asm9.tree.ClassNode;
|
||||
import org.apache.xbean.asm9.tree.FieldInsnNode;
|
||||
import org.apache.xbean.asm9.tree.FieldNode;
|
||||
import org.apache.xbean.asm9.tree.InsnList;
|
||||
import org.apache.xbean.asm9.tree.InsnNode;
|
||||
import org.apache.xbean.asm9.tree.JumpInsnNode;
|
||||
import org.apache.xbean.asm9.tree.LabelNode;
|
||||
import org.apache.xbean.asm9.tree.MethodInsnNode;
|
||||
import org.apache.xbean.asm9.tree.MethodNode;
|
||||
import org.apache.xbean.asm9.tree.TableSwitchInsnNode;
|
||||
import org.apache.xbean.asm9.tree.TypeInsnNode;
|
||||
import org.apache.xbean.asm9.tree.VarInsnNode;
|
||||
|
||||
import serp.bytecode.BCClass;
|
||||
import serp.bytecode.BCClassLoader;
|
||||
import serp.bytecode.BCField;
|
||||
import serp.bytecode.BCMethod;
|
||||
import serp.bytecode.Code;
|
||||
import serp.bytecode.Constants;
|
||||
import serp.bytecode.Instruction;
|
||||
import serp.bytecode.JumpInstruction;
|
||||
import serp.bytecode.LoadInstruction;
|
||||
import serp.bytecode.Project;
|
||||
import serp.bytecode.TableSwitchInstruction;
|
||||
|
||||
|
||||
/**
|
||||
* Factory for creating new {@link DynamicStorage} classes. Can be
|
||||
* extended to decorate/modify the generated instances behavior.
|
||||
*
|
||||
* @author Steve Kim
|
||||
* @author Mark Struberg rework to ASM
|
||||
* @since 0.3.2.0
|
||||
*/
|
||||
public class DynamicStorageGenerator {
|
||||
|
@ -95,11 +105,8 @@ public class DynamicStorageGenerator {
|
|||
};
|
||||
|
||||
// the project/classloader for the classes.
|
||||
private final Project _project = new Project();
|
||||
private final BCClassLoader _loader =
|
||||
AccessController.doPrivileged(SerpPrivacyHelper.newBCClassLoaderAction(
|
||||
_project, AccessController.doPrivileged(J2DoPrivHelper
|
||||
.getClassLoaderAction(DynamicStorage.class))));
|
||||
private final EnhancementProject _project = new EnhancementProject();
|
||||
private final EnhancementClassLoader _loader = new EnhancementClassLoader(_project, DynamicStorage.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Generate a generic {@link DynamicStorage} instance with the given
|
||||
|
@ -111,9 +118,9 @@ public class DynamicStorageGenerator {
|
|||
return null;
|
||||
|
||||
String name = getClassName(obj);
|
||||
BCClass bc = _project.loadClass(name);
|
||||
ClassNodeTracker bc = _project.loadClass(name);
|
||||
declareClasses(bc);
|
||||
bc.addDefaultConstructor().makePublic();
|
||||
addDefaultConstructor(bc);
|
||||
|
||||
int objectCount = declareFields(types, bc);
|
||||
addFactoryMethod(bc);
|
||||
|
@ -125,6 +132,26 @@ public class DynamicStorageGenerator {
|
|||
return createFactory(bc);
|
||||
}
|
||||
|
||||
private void addDefaultConstructor(ClassNodeTracker cnt) {
|
||||
ClassNode classNode = cnt.getClassNode();
|
||||
// find the default constructor
|
||||
final boolean hasDefaultCt = classNode.methods.stream()
|
||||
.anyMatch(m -> m.name.equals("<init>") && m.desc.equals("()V"));
|
||||
if (!hasDefaultCt) {
|
||||
MethodNode ctNode = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE),
|
||||
null, null);
|
||||
ctNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||
ctNode.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, classNode.superName,
|
||||
"<init>", "()V"));
|
||||
ctNode.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
classNode.methods.add(ctNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a class name to use for the given user key. By default,
|
||||
* returns the stringified key prefixed by PREFIX.
|
||||
|
@ -138,7 +165,7 @@ public class DynamicStorageGenerator {
|
|||
* {@link Constants}.
|
||||
*/
|
||||
protected int getFieldAccess() {
|
||||
return Constants.ACCESS_PRIVATE;
|
||||
return Opcodes.ACC_PRIVATE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,18 +186,17 @@ public class DynamicStorageGenerator {
|
|||
/**
|
||||
* Decorate the generated class.
|
||||
*/
|
||||
protected void decorate(Object obj, BCClass cls, int[] types) {
|
||||
protected void decorate(Object obj, ClassNodeTracker cls, int[] types) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stub factory instance for the given class.
|
||||
*/
|
||||
protected DynamicStorage createFactory(BCClass bc) {
|
||||
protected DynamicStorage createFactory(ClassNodeTracker bc) {
|
||||
try {
|
||||
Class cls = Class.forName(bc.getName(), false, _loader);
|
||||
Class cls = Class.forName(bc.getClassNode().name.replace("/", "."), false, _loader);
|
||||
Constructor cons = cls.getConstructor((Class[]) null);
|
||||
DynamicStorage data = (DynamicStorage) cons.newInstance
|
||||
((Object[]) null);
|
||||
DynamicStorage data = (DynamicStorage) cons.newInstance((Object[]) null);
|
||||
_project.clear(); // remove old refs
|
||||
return data;
|
||||
} catch (Throwable t) {
|
||||
|
@ -181,74 +207,90 @@ public class DynamicStorageGenerator {
|
|||
/**
|
||||
* Add interface or superclass declarations to the generated class.
|
||||
*/
|
||||
protected void declareClasses(BCClass bc) {
|
||||
protected void declareClasses(ClassNodeTracker bc) {
|
||||
bc.declareInterface(DynamicStorage.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the newInstance method.
|
||||
*/
|
||||
private void addFactoryMethod(BCClass bc) {
|
||||
BCMethod method = bc.declareMethod("newInstance",
|
||||
DynamicStorage.class, null);
|
||||
Code code = method.getCode(true);
|
||||
code.anew().setType(bc);
|
||||
code.dup();
|
||||
code.invokespecial().setMethod(bc.getName(), "<init>", "void", null);
|
||||
code.areturn();
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
private void addFactoryMethod(ClassNodeTracker bc) {
|
||||
final ClassNode classNode = bc.getClassNode();
|
||||
MethodNode method = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(DynamicStorage.class)),
|
||||
null, null);
|
||||
classNode.methods.add(method);
|
||||
InsnList instructions = method.instructions;
|
||||
instructions.add(new TypeInsnNode(Opcodes.NEW, classNode.name));
|
||||
instructions.add(new InsnNode(Opcodes.DUP));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||
classNode.name,
|
||||
"<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE)));
|
||||
instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement getFieldCount/getObjectCount.
|
||||
*/
|
||||
private void addFieldCount(BCClass bc, int[] types, int objectCount) {
|
||||
BCMethod method = bc.declareMethod("getFieldCount", int.class, null);
|
||||
Code code = method.getCode(true);
|
||||
code.constant().setValue(types.length);
|
||||
code.ireturn();
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
private void addFieldCount(ClassNodeTracker bc, int[] types, int objectCount) {
|
||||
final ClassNode classNode = bc.getClassNode();
|
||||
MethodNode getFc = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"getFieldCount",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE),
|
||||
null, null);
|
||||
classNode.methods.add(getFc);
|
||||
getFc.instructions.add(AsmHelper.getLoadConstantInsn(types.length));
|
||||
getFc.instructions.add(new InsnNode(Opcodes.IRETURN));
|
||||
|
||||
method = bc.declareMethod("getObjectCount", int.class, null);
|
||||
code = method.getCode(true);
|
||||
code.constant().setValue(objectCount);
|
||||
code.ireturn();
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
MethodNode getOc = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"getObjectCount",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE),
|
||||
null, null);
|
||||
classNode.methods.add(getOc);
|
||||
getOc.instructions.add(AsmHelper.getLoadConstantInsn(objectCount));
|
||||
getOc.instructions.add(new InsnNode(Opcodes.IRETURN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement initialize.
|
||||
*/
|
||||
private void addInitialize(BCClass bc, int objectCount) {
|
||||
BCMethod meth = bc.declareMethod("initialize", void.class, null);
|
||||
Code code = meth.getCode(true);
|
||||
JumpInstruction ifins = null;
|
||||
private void addInitialize(ClassNodeTracker bc, int objectCount) {
|
||||
final ClassNode classNode = bc.getClassNode();
|
||||
MethodNode initMeth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"initialize",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE),
|
||||
null, null);
|
||||
classNode.methods.add(initMeth);
|
||||
InsnList instructions = initMeth.instructions;
|
||||
|
||||
LabelNode lblEndIf = null;
|
||||
if (objectCount > 0) {
|
||||
// if (objects == null)
|
||||
// objects = new Object[objectCount];
|
||||
code.aload().setThis();
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
ifins = code.ifnonnull();
|
||||
code.aload().setThis();
|
||||
code.constant().setValue(objectCount);
|
||||
code.anewarray().setType(Object.class);
|
||||
code.putfield().setField("objects", Object[].class);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
lblEndIf = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndIf));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(objectCount));
|
||||
instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, Type.getInternalName(Object.class)));
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
}
|
||||
Instruction ins = code.vreturn();
|
||||
if (ifins != null)
|
||||
ifins.setTarget(ins);
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
|
||||
if (lblEndIf != null) {
|
||||
instructions.add(lblEndIf);
|
||||
}
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare the primitive fields and the object field.
|
||||
*/
|
||||
private int declareFields(int[] types, BCClass bc) {
|
||||
bc.declareField("objects", Object[].class).makePrivate();
|
||||
private int declareFields(int[] types, ClassNodeTracker bc) {
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE, "objects", Type.getDescriptor(Object[].class), null, null));
|
||||
|
||||
int objectCount = 0;
|
||||
Class type;
|
||||
|
@ -257,8 +299,7 @@ public class DynamicStorageGenerator {
|
|||
if (type == Object.class)
|
||||
objectCount++;
|
||||
else {
|
||||
BCField field = bc.declareField(getFieldName(i), type);
|
||||
field.setAccessFlags(getFieldAccess());
|
||||
classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE, getFieldName(i), Type.getDescriptor(type), null, null));
|
||||
}
|
||||
}
|
||||
return objectCount;
|
||||
|
@ -267,7 +308,7 @@ public class DynamicStorageGenerator {
|
|||
/**
|
||||
* Add all the typed set by index method.
|
||||
*/
|
||||
private void addSetMethods(BCClass bc, int[] types, int totalObjects) {
|
||||
private void addSetMethods(ClassNodeTracker bc, int[] types, int totalObjects) {
|
||||
for (int type : TYPES) {
|
||||
addSetMethod(type, bc, types, totalObjects);
|
||||
}
|
||||
|
@ -276,77 +317,104 @@ public class DynamicStorageGenerator {
|
|||
/**
|
||||
* Add the typed set by index method.
|
||||
*/
|
||||
private void addSetMethod(int typeCode, BCClass bc, int[] types,
|
||||
int totalObjects) {
|
||||
private void addSetMethod(int typeCode, ClassNodeTracker bc, int[] types, int totalObjects) {
|
||||
int handle = getCreateFieldMethods(typeCode);
|
||||
if (handle == POLICY_EMPTY)
|
||||
if (handle == POLICY_EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
Class type = forType(typeCode);
|
||||
|
||||
// public void set<Type> (int field, <type> val)
|
||||
String name = Object.class.equals(type) ? "Object" : StringUtil.capitalize(type.getName());
|
||||
name = "set" + name;
|
||||
BCMethod method = bc.declareMethod(name, void.class,
|
||||
new Class[]{ int.class, type });
|
||||
method.makePublic();
|
||||
Code code = method.getCode(true);
|
||||
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
MethodNode method = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
name,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(type)),
|
||||
null, null);
|
||||
classNode.methods.add(method);
|
||||
InsnList instructions = method.instructions;
|
||||
|
||||
// switch (field)
|
||||
code.aload().setParam(0);
|
||||
TableSwitchInstruction tabins = code.tableswitch();
|
||||
tabins.setLow(0);
|
||||
tabins.setHigh(types.length - 1);
|
||||
Instruction defaultIns;
|
||||
if (handle == POLICY_SILENT) {
|
||||
defaultIns = code.vreturn();
|
||||
}
|
||||
else {
|
||||
defaultIns = throwException(code, IllegalArgumentException.class);
|
||||
}
|
||||
tabins.setDefaultTarget(defaultIns);
|
||||
instructions.add(new VarInsnNode(Opcodes.ILOAD, 1)); // switch on first parameter which is an int
|
||||
|
||||
LabelNode defLbl = new LabelNode();
|
||||
TableSwitchInsnNode switchNd = new TableSwitchInsnNode(0, types.length - 1, defLbl);
|
||||
instructions.add(switchNd);
|
||||
|
||||
|
||||
int objectCount = 0;
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
// default: throw new IllegalArgumentException
|
||||
if (!isCompatible(types[i], typeCode)) {
|
||||
tabins.addTarget(tabins.getDefaultTarget());
|
||||
LabelNode caseLbl = new LabelNode();
|
||||
switchNd.labels.add(caseLbl);
|
||||
instructions.add(caseLbl);
|
||||
instructions.add(getDefaultSetInstructions(handle));
|
||||
continue;
|
||||
}
|
||||
|
||||
tabins.addTarget(code.aload().setThis());
|
||||
LabelNode caseLbl = new LabelNode();
|
||||
switchNd.labels.add(caseLbl);
|
||||
instructions.add(caseLbl);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
|
||||
if (typeCode >= JavaTypes.OBJECT) {
|
||||
// if (objects == null)
|
||||
// objects = new Object[totalObjects];
|
||||
code.aload().setThis();
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
JumpInstruction ifins = code.ifnonnull();
|
||||
code.aload().setThis();
|
||||
code.constant().setValue(totalObjects);
|
||||
code.anewarray().setType(Object.class);
|
||||
code.putfield().setField("objects", Object[].class);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
|
||||
LabelNode lblEndNonNull = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndNonNull));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(totalObjects));
|
||||
instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY, Type.getInternalName(Object.class)));
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
|
||||
instructions.add(lblEndNonNull);
|
||||
|
||||
// objects[objectCount] = val;
|
||||
ifins.setTarget(code.aload().setThis());
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
code.constant().setValue(objectCount);
|
||||
code.aload().setParam(1);
|
||||
code.aastore();
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(objectCount));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // 2nd param
|
||||
instructions.add(new InsnNode(Opcodes.AASTORE));
|
||||
|
||||
objectCount++;
|
||||
} else {
|
||||
// case i: fieldi = val;
|
||||
LoadInstruction load = code.xload();
|
||||
load.setType(type);
|
||||
load.setParam(1);
|
||||
code.putfield().setField("field" + i, type);
|
||||
instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(type), 2)); // 2nd param is primitive
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, "field" + i, Type.getInternalName(type)));
|
||||
}
|
||||
// return
|
||||
code.vreturn();
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
|
||||
|
||||
// default:
|
||||
instructions.add(defLbl);
|
||||
instructions.add(getDefaultSetInstructions(handle));
|
||||
}
|
||||
|
||||
private static InsnList getDefaultSetInstructions(int handle) {
|
||||
InsnList defaultInsns;
|
||||
if (handle == POLICY_SILENT) {
|
||||
defaultInsns = new InsnList();
|
||||
defaultInsns.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
else {
|
||||
defaultInsns = AsmHelper.throwException(IllegalArgumentException.class);
|
||||
}
|
||||
return defaultInsns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all typed get by index method for the given fields.
|
||||
*/
|
||||
private void addGetMethods(BCClass bc, int[] types) {
|
||||
private void addGetMethods(ClassNodeTracker bc, int[] types) {
|
||||
for (int type : TYPES) {
|
||||
addGetMethod(type, bc, types);
|
||||
}
|
||||
|
@ -355,121 +423,139 @@ public class DynamicStorageGenerator {
|
|||
/**
|
||||
* Add typed get by index method.
|
||||
*/
|
||||
private void addGetMethod(int typeCode, BCClass bc, int[] types) {
|
||||
private void addGetMethod(int typeCode, ClassNodeTracker bc, int[] types) {
|
||||
int handle = getCreateFieldMethods(typeCode);
|
||||
if (handle == POLICY_EMPTY)
|
||||
if (handle == POLICY_EMPTY) {
|
||||
return;
|
||||
}
|
||||
Class type = forType(typeCode);
|
||||
|
||||
// public <type> get<Type>Field (int field)
|
||||
String name = Object.class.equals(type) ? "Object" :
|
||||
StringUtil.capitalize(type.getName());
|
||||
name = "get" + name;
|
||||
BCMethod method = bc.declareMethod(name, type,
|
||||
new Class[]{ int.class });
|
||||
method.makePublic();
|
||||
Code code = method.getCode(true);
|
||||
String name = "get" + (Object.class.equals(type) ? "Object" : StringUtil.capitalize(type.getName()));
|
||||
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
MethodNode meth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
name,
|
||||
Type.getMethodDescriptor(Type.getType(type), Type.INT_TYPE),
|
||||
null, null);
|
||||
classNode.methods.add(meth);
|
||||
InsnList instructions = meth.instructions;
|
||||
|
||||
// switch (field)
|
||||
code.aload().setParam(0);
|
||||
TableSwitchInstruction tabins = code.tableswitch();
|
||||
tabins.setLow(0);
|
||||
tabins.setHigh(types.length - 1);
|
||||
Instruction defaultIns = null;
|
||||
if (typeCode == JavaTypes.OBJECT && handle == POLICY_SILENT) {
|
||||
defaultIns = code.constant().setNull();
|
||||
code.areturn();
|
||||
} else
|
||||
defaultIns = throwException
|
||||
(code, IllegalArgumentException.class);
|
||||
tabins.setDefaultTarget(defaultIns);
|
||||
instructions.add(new VarInsnNode(Opcodes.ILOAD, 1)); // switch on first parameter which is an int
|
||||
|
||||
LabelNode defLbl = new LabelNode();
|
||||
TableSwitchInsnNode switchNd = new TableSwitchInsnNode(0, types.length - 1, defLbl);
|
||||
instructions.add(switchNd);
|
||||
|
||||
|
||||
int objectCount = 0;
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
// default: throw new IllegalArgumentException
|
||||
if (!isCompatible(types[i], typeCode)) {
|
||||
tabins.addTarget(tabins.getDefaultTarget());
|
||||
LabelNode caseLbl = new LabelNode();
|
||||
switchNd.labels.add(caseLbl);
|
||||
instructions.add(caseLbl);
|
||||
instructions.add(getDefaultGetInstructions(typeCode, handle));
|
||||
continue;
|
||||
}
|
||||
|
||||
tabins.addTarget(code.aload().setThis());
|
||||
LabelNode caseLbl = new LabelNode();
|
||||
switchNd.labels.add(caseLbl);
|
||||
instructions.add(caseLbl);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
if (typeCode >= JavaTypes.OBJECT) {
|
||||
// if (objects == null)
|
||||
// return null;
|
||||
// return objects[objectCount];
|
||||
code.aload().setThis();
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
JumpInstruction ifins = code.ifnonnull();
|
||||
code.constant().setNull();
|
||||
code.areturn();
|
||||
ifins.setTarget(code.aload().setThis());
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
code.constant().setValue(objectCount);
|
||||
code.aaload();
|
||||
code.areturn();
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
|
||||
LabelNode lblEndNonNull = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndNonNull));
|
||||
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||
|
||||
instructions.add(lblEndNonNull);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "objects", Type.getDescriptor(Object[].class)));
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(objectCount));
|
||||
instructions.add(new InsnNode(Opcodes.AALOAD));
|
||||
instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||
|
||||
objectCount++;
|
||||
} else {
|
||||
// case i: return fieldi;
|
||||
code.getfield().setField("field" + i, type);
|
||||
code.xreturn().setType(type);
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, "field" + i, Type.getInternalName(type)));
|
||||
instructions.add(new InsnNode(AsmHelper.getReturnInsn(type)));
|
||||
}
|
||||
}
|
||||
code.calculateMaxLocals();
|
||||
code.calculateMaxStack();
|
||||
|
||||
// default:
|
||||
instructions.add(defLbl);
|
||||
instructions.add(getDefaultGetInstructions(typeCode, handle));
|
||||
}
|
||||
|
||||
private static InsnList getDefaultGetInstructions(int typeCode, int handle) {
|
||||
InsnList defaultInsns;
|
||||
if (typeCode == JavaTypes.OBJECT && handle == POLICY_SILENT) {
|
||||
defaultInsns = new InsnList();
|
||||
defaultInsns.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
defaultInsns.add(new InsnNode(Opcodes.ARETURN));
|
||||
}
|
||||
else {
|
||||
defaultInsns = AsmHelper.throwException(IllegalArgumentException.class);
|
||||
}
|
||||
return defaultInsns;
|
||||
}
|
||||
|
||||
/////////////
|
||||
// Utilities
|
||||
/////////////
|
||||
|
||||
/**
|
||||
* Clear code associated with the given method signature, and return
|
||||
* the empty code. Will return null if the method should be empty.
|
||||
*/
|
||||
protected Code replaceMethod(BCClass bc, String name, Class retType,
|
||||
Class[] args, boolean remove) {
|
||||
bc.removeDeclaredMethod(name, args);
|
||||
BCMethod meth = bc.declareMethod(name, retType, args);
|
||||
Code code = meth.getCode(true);
|
||||
if (!remove)
|
||||
return code;
|
||||
code.xreturn().setType(retType);
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bean field of the given name and type.
|
||||
*/
|
||||
protected BCField addBeanField(BCClass bc, String name, Class type) {
|
||||
if (name == null)
|
||||
protected FieldNode addBeanField(ClassNodeTracker bc, String name, Class type) {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("name == null");
|
||||
}
|
||||
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
|
||||
// private <type> <name>
|
||||
BCField field = bc.declareField(name, type);
|
||||
field.setAccessFlags(getFieldAccess());
|
||||
FieldNode field = new FieldNode(getFieldAccess(), name, Type.getDescriptor(type), null, null);
|
||||
classNode.fields.add(field);
|
||||
|
||||
name = StringUtil.capitalize(name);
|
||||
|
||||
// getter
|
||||
String prefix = (type == boolean.class) ? "is" : "get";
|
||||
BCMethod method = bc.declareMethod(prefix + name, type, null);
|
||||
method.makePublic();
|
||||
Code code = method.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.getfield().setField(field);
|
||||
code.xreturn().setType(type);
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
{
|
||||
String prefix = (type == boolean.class) ? "is" : "get";
|
||||
MethodNode meth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
prefix + name,
|
||||
Type.getMethodDescriptor(Type.getType(type)),
|
||||
null, null);
|
||||
classNode.methods.add(meth);
|
||||
meth.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
meth.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, field.name, Type.getDescriptor(type)));
|
||||
meth.instructions.add(new InsnNode(AsmHelper.getReturnInsn(type)));
|
||||
}
|
||||
|
||||
// setter
|
||||
method = bc.declareMethod("set" + name, void.class,
|
||||
new Class[]{ type });
|
||||
method.makePublic();
|
||||
code = method.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.xload().setParam(0).setType(type);
|
||||
code.putfield().setField(field);
|
||||
code.vreturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
{
|
||||
MethodNode meth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"set" + name,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(type)),
|
||||
null, null);
|
||||
classNode.methods.add(meth);
|
||||
|
||||
meth.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
meth.instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(type), 1)); // value parameter
|
||||
meth.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, Type.getDescriptor(type)));
|
||||
meth.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
|
@ -530,9 +616,10 @@ public class DynamicStorageGenerator {
|
|||
*/
|
||||
protected Class getWrapper(Class c) {
|
||||
for (Class[] wrapper : WRAPPERS) {
|
||||
if (wrapper[0].equals(c))
|
||||
if (wrapper[0].equals(c)) {
|
||||
return wrapper[1];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,16 +37,31 @@ import org.apache.openjpa.meta.ClassMetaData;
|
|||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.util.InternalException;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.asm.ClassNodeTracker;
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
import org.apache.xbean.asm9.Type;
|
||||
import org.apache.xbean.asm9.tree.ClassNode;
|
||||
import org.apache.xbean.asm9.tree.FieldInsnNode;
|
||||
import org.apache.xbean.asm9.tree.FieldNode;
|
||||
import org.apache.xbean.asm9.tree.InsnList;
|
||||
import org.apache.xbean.asm9.tree.InsnNode;
|
||||
import org.apache.xbean.asm9.tree.JumpInsnNode;
|
||||
import org.apache.xbean.asm9.tree.LabelNode;
|
||||
import org.apache.xbean.asm9.tree.MethodInsnNode;
|
||||
import org.apache.xbean.asm9.tree.MethodNode;
|
||||
import org.apache.xbean.asm9.tree.TypeInsnNode;
|
||||
import org.apache.xbean.asm9.tree.VarInsnNode;
|
||||
|
||||
import serp.bytecode.BCClass;
|
||||
import serp.bytecode.BCField;
|
||||
import serp.bytecode.BCMethod;
|
||||
import serp.bytecode.Code;
|
||||
import serp.bytecode.Constants;
|
||||
import serp.bytecode.ExceptionHandler;
|
||||
import serp.bytecode.Instruction;
|
||||
import serp.bytecode.JumpInstruction;
|
||||
import serp.bytecode.LookupSwitchInstruction;
|
||||
import serp.bytecode.Project;
|
||||
|
||||
/**
|
||||
* Generates {@link PCData} instances which avoid primitive wrappers
|
||||
|
@ -54,10 +69,10 @@ import serp.bytecode.LookupSwitchInstruction;
|
|||
* startup time.
|
||||
*
|
||||
* @author Steve Kim
|
||||
* @author Mark Struberg rework to ASM
|
||||
* @since 0.3.2
|
||||
*/
|
||||
public class PCDataGenerator
|
||||
extends DynamicStorageGenerator {
|
||||
public class PCDataGenerator extends DynamicStorageGenerator {
|
||||
|
||||
private static final Localizer _loc = Localizer.forPackage
|
||||
(PCDataGenerator.class);
|
||||
|
@ -130,10 +145,10 @@ public class PCDataGenerator
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void declareClasses(BCClass bc) {
|
||||
protected void declareClasses(ClassNodeTracker bc) {
|
||||
super.declareClasses(bc);
|
||||
bc.declareInterface(DynamicPCData.class);
|
||||
bc.setSuperclass(AbstractPCData.class);
|
||||
bc.getClassNode().superName = Type.getInternalName(AbstractPCData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,52 +164,66 @@ public class PCDataGenerator
|
|||
}
|
||||
|
||||
@Override
|
||||
protected final void decorate(Object obj, BCClass bc, int[] types) {
|
||||
protected final void decorate(Object obj, ClassNodeTracker bc, int[] types) {
|
||||
super.decorate(obj, bc, types);
|
||||
ClassMetaData meta = (ClassMetaData) obj;
|
||||
|
||||
enhanceConstructor(bc);
|
||||
addBaseFields(bc);
|
||||
addImplDataMethods(bc, meta);
|
||||
addFieldImplDataMethods(bc, meta);
|
||||
addVersionMethods(bc);
|
||||
addGetType(bc, meta);
|
||||
addLoadMethod(bc, meta);
|
||||
addLoadWithFieldsMethod(bc, meta);
|
||||
addStoreMethods(bc, meta);
|
||||
addNewEmbedded(bc);
|
||||
addGetData(bc);
|
||||
addVersionMethods(bc);
|
||||
|
||||
BCClass _bc = new Project().loadClass(bc.getClassNode().name.replace("/", "."));
|
||||
AsmHelper.readIntoBCClass(bc, _bc);
|
||||
|
||||
addFieldImplDataMethods(_bc, meta);
|
||||
addLoadMethod(_bc, meta);
|
||||
addLoadWithFieldsMethod(_bc, meta);
|
||||
addStoreMethods(_bc, meta);
|
||||
addNewEmbedded(_bc);
|
||||
addGetData(_bc);
|
||||
|
||||
bc = AsmHelper.toClassNode(bc.getProject(), _bc);
|
||||
|
||||
decorate(bc, meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply additional decoration to generated class.
|
||||
*/
|
||||
protected void decorate(BCClass bc, ClassMetaData meta) {
|
||||
protected void decorate(ClassNodeTracker bc, ClassMetaData meta) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhance constructor to initialize fields
|
||||
*/
|
||||
private void enhanceConstructor(BCClass bc) {
|
||||
BCMethod cons = bc.getDeclaredMethod("<init>", (String[]) null);
|
||||
Code code = cons.getCode(false);
|
||||
code.afterLast();
|
||||
code.previous();
|
||||
private void enhanceConstructor(ClassNodeTracker bc) {
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
|
||||
// find the default constructor
|
||||
MethodNode defaultCt = classNode.methods.stream()
|
||||
.filter(m -> m.name.equals("<init>") && m.desc.equals("()V"))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
InsnList instructions = new InsnList();
|
||||
|
||||
|
||||
// private BitSet loaded = new BitSet();
|
||||
BCField loaded = addBeanField(bc, "loaded", BitSet.class);
|
||||
loaded.setFinal(true);
|
||||
code.aload().setThis();
|
||||
code.anew().setType(BitSet.class);
|
||||
code.dup();
|
||||
code.constant().setValue(bc.getFields().length);
|
||||
code.invokespecial().setMethod(BitSet.class, "<init>", void.class,
|
||||
new Class[]{ int.class });
|
||||
code.putfield().setField(loaded);
|
||||
FieldNode loaded = addBeanField(bc, "loaded", BitSet.class);
|
||||
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(BitSet.class)));
|
||||
instructions.add(new InsnNode(Opcodes.DUP));
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(classNode.fields.size()));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||
Type.getInternalName(BitSet.class),
|
||||
"<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE)));
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, loaded.name, loaded.desc));
|
||||
|
||||
defaultCt.instructions.insertBefore(defaultCt.instructions.getLast(), instructions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,123 +231,143 @@ public class PCDataGenerator
|
|||
* same classloader (i.e. rar vs. ear). The context classloader
|
||||
* (i.e. the user app classloader) should be fine.
|
||||
*/
|
||||
private void addGetType(BCClass bc, ClassMetaData meta) {
|
||||
BCField type = bc.declareField("type", Class.class);
|
||||
type.setStatic(true);
|
||||
type.makePrivate();
|
||||
private void addGetType(ClassNodeTracker bc, ClassMetaData meta) {
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
FieldNode typeField = new FieldNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "type", Type.getDescriptor(Class.class), null, null);
|
||||
classNode.fields.add(typeField);
|
||||
|
||||
// public Class getType() {
|
||||
BCMethod getter = bc.declareMethod("getType", Class.class, null);
|
||||
getter.makePublic();
|
||||
Code code = getter.getCode(true);
|
||||
MethodNode getter = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"getType",
|
||||
Type.getMethodDescriptor(Type.getType(Class.class)),
|
||||
null, null);
|
||||
classNode.methods.add(getter);
|
||||
|
||||
InsnList instructions = getter.instructions;
|
||||
|
||||
// use name as constant filled with meta.getDescribedType().getName()
|
||||
// if (type == null) {
|
||||
// try {
|
||||
// type = Class.forName
|
||||
// (meta.getDescribedType().getName(), true,
|
||||
// Thread.currentThread().getContextClassLoader());
|
||||
// } catch (ClassNotFoundException cnfe) {
|
||||
// throw new InternalException();
|
||||
// }
|
||||
// type = PCDataGenerator.getType(name)
|
||||
// }
|
||||
code.getstatic().setField(type);
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, typeField.name, typeField.desc));
|
||||
|
||||
Collection<Instruction> jumps = new LinkedList<>();
|
||||
jumps.add(code.ifnonnull());
|
||||
ExceptionHandler handler = code.addExceptionHandler();
|
||||
LabelNode lblEndIfNN = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndIfNN));
|
||||
|
||||
handler.setTryStart(code.constant().setValue
|
||||
(meta.getDescribedType().getName()));
|
||||
code.constant().setValue(true);
|
||||
code.invokestatic().setMethod(Thread.class, "currentThread",
|
||||
Thread.class, null);
|
||||
code.invokevirtual().setMethod(Thread.class, "getContextClassLoader",
|
||||
ClassLoader.class, null);
|
||||
code.invokestatic().setMethod(Class.class, "forName", Class.class,
|
||||
new Class[]{ String.class, boolean.class, ClassLoader.class });
|
||||
code.putstatic().setField(type);
|
||||
Instruction go2 = code.go2();
|
||||
jumps.add(go2);
|
||||
handler.setTryEnd(go2);
|
||||
handler.setCatch(ClassNotFoundException.class);
|
||||
handler.setHandlerStart(throwException
|
||||
(code, InternalException.class));
|
||||
setTarget(code.getstatic().setField(type), jumps);
|
||||
code.areturn();
|
||||
// actual type = PCDataGenerator.getType(name)
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(meta.getDescribedType().getName()));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
|
||||
Type.getInternalName(PCDataGenerator.class),
|
||||
"getType",
|
||||
Type.getMethodDescriptor(Type.getType(Class.class), Type.getType(String.class))));
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, typeField.name, typeField.desc));
|
||||
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
instructions.add(lblEndIfNN);
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, typeField.name, typeField.desc));
|
||||
instructions.add(new InsnNode(Opcodes.ARETURN));
|
||||
}
|
||||
|
||||
public static Class<?> getType(String className) {
|
||||
try {
|
||||
return Class.forName(className, true, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
throw new InternalException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare standard dynamic pcdata fields.
|
||||
*/
|
||||
private void addBaseFields(BCClass bc) {
|
||||
private void addBaseFields(ClassNodeTracker bc) {
|
||||
addBeanField(bc, "id", Object.class);
|
||||
BCField field = addBeanField(bc, "storageGenerator",
|
||||
PCDataGenerator.class);
|
||||
field.setAccessFlags(field.getAccessFlags()
|
||||
| Constants.ACCESS_TRANSIENT);
|
||||
FieldNode field = addBeanField(bc, "storageGenerator", PCDataGenerator.class);
|
||||
field.access |= Constants.ACCESS_TRANSIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add methods for loading and storing class-level impl data.
|
||||
*/
|
||||
private void addImplDataMethods(BCClass bc, ClassMetaData meta) {
|
||||
// void storeImplData(OpenJPAStateManager);
|
||||
BCMethod meth = bc.declareMethod("storeImplData", void.class,
|
||||
new Class[]{ OpenJPAStateManager.class });
|
||||
Code code = meth.getCode(true);
|
||||
private void addImplDataMethods(ClassNodeTracker bc, ClassMetaData meta) {
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
|
||||
BCField impl = null;
|
||||
if (!usesImplData(meta))
|
||||
code.vreturn();
|
||||
// void storeImplData(OpenJPAStateManager);
|
||||
MethodNode storeM = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"storeImplData",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class)),
|
||||
null, null);
|
||||
classNode.methods.add(storeM);
|
||||
InsnList instructions = storeM.instructions;
|
||||
|
||||
FieldNode impl = null;
|
||||
if (!usesImplData(meta)) {
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
else {
|
||||
// if (sm.isImplDataCacheable())
|
||||
// setImplData(sm.getImplData());
|
||||
// setImplData(sm.getImplData());
|
||||
impl = addBeanField(bc, "implData", Object.class);
|
||||
code.aload().setParam(0);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
"isImplDataCacheable", boolean.class, null);
|
||||
JumpInstruction ifins = code.ifeq();
|
||||
code.aload().setThis();
|
||||
code.aload().setParam(0);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
"getImplData", Object.class, null);
|
||||
code.invokevirtual().setMethod("setImplData", void.class,
|
||||
new Class[]{ Object.class });
|
||||
ifins.setTarget(code.vreturn());
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"isImplDataCacheable",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE)));
|
||||
LabelNode lblEndIfEq = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFEQ, lblEndIfEq));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"getImplData",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))));
|
||||
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||
classNode.name,
|
||||
"setImplData",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class))));
|
||||
|
||||
instructions.add(lblEndIfEq);
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
|
||||
// void loadImplData(OpenJPAStateManager);
|
||||
meth = bc.declareMethod("loadImplData", void.class,
|
||||
new Class[]{ OpenJPAStateManager.class });
|
||||
code = meth.getCode(true);
|
||||
if (!usesImplData(meta))
|
||||
code.vreturn();
|
||||
MethodNode loadM = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"loadImplData",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class)),
|
||||
null, null);
|
||||
classNode.methods.add(loadM);
|
||||
instructions = loadM.instructions;
|
||||
|
||||
if (!usesImplData(meta)) {
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
else {
|
||||
// if (sm.getImplData() == null && implData != null)
|
||||
// sm.setImplData(impl, true);
|
||||
code.aload().setParam(0);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
"getImplData", Object.class, null);
|
||||
JumpInstruction ifins = code.ifnonnull();
|
||||
code.aload().setThis();
|
||||
code.getfield().setField(impl);
|
||||
JumpInstruction ifins2 = code.ifnull();
|
||||
code.aload().setParam(0);
|
||||
code.aload().setThis();
|
||||
code.getfield().setField(impl);
|
||||
code.constant().setValue(true);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
"setImplData", void.class,
|
||||
new Class[]{ Object.class, boolean.class });
|
||||
Instruction ins = code.vreturn();
|
||||
ifins.setTarget(ins);
|
||||
ifins2.setTarget(ins);
|
||||
// sm.setImplData(impl, true);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"getImplData",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))));
|
||||
LabelNode lblEndIf = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndIf));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, impl.name, impl.desc));
|
||||
|
||||
LabelNode lblEndIf2 = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNULL, lblEndIf2));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, impl.name, impl.desc));
|
||||
instructions.add(AsmHelper.getLoadConstantInsn(true));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"setImplData",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class), Type.BOOLEAN_TYPE)));
|
||||
instructions.add(lblEndIf);
|
||||
instructions.add(lblEndIf2);
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -448,7 +497,7 @@ public class PCDataGenerator
|
|||
code.ifnull().setTarget(nullTarget);
|
||||
|
||||
// if (fieldImpl == null)
|
||||
// fieldImpl = new Object[fields];
|
||||
// fieldImpl = new Object[fields];
|
||||
code.aload().setThis();
|
||||
code.getfield().setField(impl);
|
||||
ifins = code.ifnonnull();
|
||||
|
@ -467,7 +516,7 @@ public class PCDataGenerator
|
|||
code.vreturn();
|
||||
|
||||
// if (fieldImpl != null)
|
||||
// fieldImpl[index] = null;
|
||||
// fieldImpl[index] = null;
|
||||
code.next(); // step over nullTarget
|
||||
code.getfield().setField(impl);
|
||||
ifins = code.ifnonnull();
|
||||
|
@ -486,49 +535,67 @@ public class PCDataGenerator
|
|||
/**
|
||||
* Add methods for loading and storing version data.
|
||||
*/
|
||||
protected void addVersionMethods(BCClass bc) {
|
||||
// void storeVersion(OpenJPAStateManager sm);
|
||||
addBeanField(bc, "version", Object.class);
|
||||
BCMethod meth = bc.declareMethod("storeVersion", void.class,
|
||||
new Class[]{ OpenJPAStateManager.class });
|
||||
Code code = meth.getCode(true);
|
||||
protected void addVersionMethods(ClassNodeTracker bc) {
|
||||
ClassNode classNode = bc.getClassNode();
|
||||
|
||||
// version = sm.getVersion();
|
||||
code.aload().setThis();
|
||||
code.aload().setParam(0);
|
||||
code.invokeinterface()
|
||||
.setMethod(OpenJPAStateManager.class, "getVersion",
|
||||
Object.class, null);
|
||||
code.putfield().setField("version", Object.class);
|
||||
code.vreturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
final FieldNode versionField = addBeanField(bc, "version", Object.class);
|
||||
|
||||
// void storeVersion(OpenJPAStateManager sm);
|
||||
{
|
||||
MethodNode storeMeth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"storeVersion",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class)),
|
||||
null, null);
|
||||
classNode.methods.add(storeMeth);
|
||||
InsnList instructions = storeMeth.instructions;
|
||||
|
||||
|
||||
// version = sm.getVersion();
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"getVersion",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))));
|
||||
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, versionField.name, versionField.desc));
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
|
||||
// void loadVersion(OpenJPAStateManager sm)
|
||||
meth = bc.declareMethod("loadVersion", void.class,
|
||||
new Class[]{ OpenJPAStateManager.class });
|
||||
code = meth.getCode(true);
|
||||
{
|
||||
MethodNode loadMeth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||
"loadVersion",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class)),
|
||||
null, null);
|
||||
classNode.methods.add(loadMeth);
|
||||
InsnList instructions = loadMeth.instructions;
|
||||
|
||||
// if (sm.getVersion() == null)
|
||||
// sm.setVersion(version);
|
||||
code.aload().setParam(0);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
"getVersion", Object.class, null);
|
||||
JumpInstruction ifins = code.ifnonnull();
|
||||
code.aload().setParam(0);
|
||||
code.aload().setThis();
|
||||
code.getfield().setField("version", Object.class);
|
||||
code.invokeinterface()
|
||||
.setMethod(OpenJPAStateManager.class, "setVersion",
|
||||
void.class, new Class[]{ Object.class });
|
||||
ifins.setTarget(code.vreturn());
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
// if (sm.getVersion() == null)
|
||||
// sm.setVersion(version);
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"getVersion",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))));
|
||||
|
||||
LabelNode lblEndIf = new LabelNode();
|
||||
instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblEndIf));
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, versionField.name, versionField.desc));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||
Type.getInternalName(OpenJPAStateManager.class),
|
||||
"setVersion",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class))));
|
||||
|
||||
instructions.add(lblEndIf);
|
||||
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
}
|
||||
}
|
||||
|
||||
private void addLoadMethod(BCClass bc, ClassMetaData meta) {
|
||||
// public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
|
||||
// Object context)
|
||||
// Object context)
|
||||
Code code = addLoadMethod(bc, false);
|
||||
FieldMetaData[] fmds = meta.getFields();
|
||||
Collection<Instruction> jumps = new LinkedList<>();
|
||||
|
@ -584,7 +651,7 @@ public class PCDataGenerator
|
|||
private void addLoadWithFieldsMethod(BCClass bc, ClassMetaData meta) {
|
||||
Code code = addLoadMethod(bc, true);
|
||||
// public void load(OpenJPAStateManager sm, BitSet fields,
|
||||
// FetchConfiguration fetch, Object conn)
|
||||
// FetchConfiguration fetch, Object conn)
|
||||
FieldMetaData[] fmds = meta.getFields();
|
||||
Collection<Instruction> jumps = new LinkedList<>();
|
||||
Collection<Instruction> jumps2;
|
||||
|
@ -602,7 +669,7 @@ public class PCDataGenerator
|
|||
intermediate = usesIntermediate(fmds[i]);
|
||||
// if (fields.get(i))
|
||||
// {
|
||||
// if (loaded.get(i))
|
||||
// if (loaded.get(i))
|
||||
setTarget(code.aload().setParam(1), jumps);
|
||||
code.constant().setValue(i);
|
||||
code.invokevirtual().setMethod(BitSet.class, "get",
|
||||
|
@ -706,7 +773,7 @@ public class PCDataGenerator
|
|||
FieldMetaData.class, new Class[]{ int.class });
|
||||
code.astore().setLocal(local);
|
||||
// sm.storeField(i, toField(sm, fmd, objects[objectCount],
|
||||
// fetch, context);
|
||||
// fetch, context);
|
||||
code.aload().setParam(0);
|
||||
code.constant().setValue(index);
|
||||
code.aload().setThis();
|
||||
|
@ -735,13 +802,13 @@ public class PCDataGenerator
|
|||
private Instruction addLoadIntermediate(Code code, int index,
|
||||
int objectCount, Collection<Instruction> jumps2, int inter) {
|
||||
// {
|
||||
// Object inter = objects[objectCount];
|
||||
// Object inter = objects[objectCount];
|
||||
Instruction first = code.aload().setThis();
|
||||
code.getfield().setField("objects", Object[].class);
|
||||
code.constant().setValue(objectCount);
|
||||
code.aaload();
|
||||
code.astore().setLocal(inter);
|
||||
// if (inter != null && !sm.getLoaded().get(index))
|
||||
// if (inter != null && !sm.getLoaded().get(index))
|
||||
code.aload().setLocal(inter);
|
||||
jumps2.add(code.ifnull());
|
||||
code.aload().setParam(0);
|
||||
|
@ -751,8 +818,8 @@ public class PCDataGenerator
|
|||
code.invokevirtual().setMethod(BitSet.class, "get",
|
||||
boolean.class, new Class[]{ int.class });
|
||||
jumps2.add(code.ifne());
|
||||
// sm.setIntermediate(index, inter);
|
||||
// } // end else
|
||||
// sm.setIntermediate(index, inter);
|
||||
// } // end else
|
||||
code.aload().setParam(0);
|
||||
code.constant().setValue(index);
|
||||
code.aload().setLocal(inter);
|
||||
|
@ -769,8 +836,7 @@ public class PCDataGenerator
|
|||
addStoreMethod(bc, meta, false);
|
||||
}
|
||||
|
||||
private void addStoreMethod(BCClass bc, ClassMetaData meta,
|
||||
boolean fields) {
|
||||
private void addStoreMethod(BCClass bc, ClassMetaData meta, boolean fields) {
|
||||
BCMethod store;
|
||||
if (fields)
|
||||
store = bc.declareMethod("store", void.class,
|
||||
|
@ -832,7 +898,7 @@ public class PCDataGenerator
|
|||
jumps.add(code.ifne());
|
||||
// Object val = sm.getIntermediate(index);
|
||||
// if (val != null)
|
||||
// objects[objectCount] = val;
|
||||
// objects[objectCount] = val;
|
||||
code.aload().setParam(0);
|
||||
code.constant().setValue(i);
|
||||
code.invokeinterface().setMethod(OpenJPAStateManager.class,
|
||||
|
@ -876,7 +942,7 @@ public class PCDataGenerator
|
|||
new Class[]{ int.class });
|
||||
} else {
|
||||
// Object val = toData(sm.getMetaData().getField(index),
|
||||
// sm.fetchField(index, false), sm.getContext());
|
||||
// sm.fetchField(index, false), sm.getContext());
|
||||
int local = code.getNextLocalsIndex();
|
||||
code.aload().setThis();
|
||||
code.aload().setParam(0);
|
||||
|
@ -900,11 +966,11 @@ public class PCDataGenerator
|
|||
code.astore().setLocal(local);
|
||||
|
||||
// if (val == NULL) {
|
||||
// val = null;
|
||||
// loaded.clear(index);
|
||||
// } else
|
||||
// loaded.set(index);
|
||||
// objects[objectCount] = val;
|
||||
// val = null;
|
||||
// loaded.clear(index);
|
||||
// } else
|
||||
// loaded.set(index);
|
||||
// objects[objectCount] = val;
|
||||
code.aload().setLocal(local);
|
||||
code.getstatic().setField(AbstractPCData.class, "NULL",
|
||||
Object.class);
|
||||
|
@ -950,7 +1016,7 @@ public class PCDataGenerator
|
|||
new Class[]{ OpenJPAStateManager.class });
|
||||
Code code = meth.getCode(true);
|
||||
// return getStorageGenerator().generatePCData
|
||||
// (sm.getId(), sm.getMetaData());
|
||||
// (sm.getId(), sm.getMetaData());
|
||||
code.aload().setThis();
|
||||
code.getfield().setField("storageGenerator", PCDataGenerator.class);
|
||||
code.aload().setParam(0);
|
||||
|
|
|
@ -26,10 +26,8 @@ import org.apache.openjpa.lib.util.Localizer.Message;
|
|||
* @author Abe White
|
||||
* @since 0.3.2
|
||||
*/
|
||||
public class InternalException
|
||||
extends OpenJPAException {
|
||||
public class InternalException extends OpenJPAException {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InternalException() {
|
||||
|
|
|
@ -35,8 +35,7 @@ import org.apache.openjpa.lib.util.Localizer.Message;
|
|||
* @author Abe White
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public abstract class OpenJPAException
|
||||
extends RuntimeException
|
||||
public abstract class OpenJPAException extends RuntimeException
|
||||
implements Serializable, ExceptionInfo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.apache.xbean.asm9.tree.ClassNode;
|
|||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
*/
|
||||
public class ClassNodeTracker {
|
||||
private final ClassNode classNode;
|
||||
private ClassNode classNode;
|
||||
private final ClassLoader cl;
|
||||
private final EnhancementProject project;
|
||||
|
||||
|
@ -60,6 +60,10 @@ public class ClassNodeTracker {
|
|||
return classNode;
|
||||
}
|
||||
|
||||
public void setClassNode(ClassNode classNode) {
|
||||
this.classNode = classNode;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return cl;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@ public class TestDynamicStorageGenerator
|
|||
super(s, "enhancecactusapp");
|
||||
}
|
||||
|
||||
public void testStorage()
|
||||
throws Exception {
|
||||
public void testStorage() throws Exception {
|
||||
|
||||
DynamicStorageGenerator gen = new DynamicStorageGenerator();
|
||||
int[] types = new int[]{
|
||||
|
@ -48,8 +47,7 @@ public class TestDynamicStorageGenerator
|
|||
JavaTypes.STRING,
|
||||
JavaTypes.OBJECT
|
||||
};
|
||||
DynamicStorage storage = gen.generateStorage(types,
|
||||
"org.apache.openjpa.enhance.Test");
|
||||
DynamicStorage storage = gen.generateStorage(types, "org.apache.openjpa.enhance.Test");
|
||||
storage = storage.newInstance();
|
||||
|
||||
storage.setBoolean(0, true);
|
||||
|
@ -71,8 +69,7 @@ public class TestDynamicStorageGenerator
|
|||
assertEquals(4, storage.getShort(4));
|
||||
assertEquals(5, storage.getLong(5));
|
||||
assertTrue(6.59 < storage.getFloat(6) && 6.61 > storage.getFloat(6));
|
||||
assertTrue(7.69 < storage.getDouble(7)
|
||||
&& 7.71 > storage.getDouble(7));
|
||||
assertTrue(7.69 < storage.getDouble(7) && 7.71 > storage.getDouble(7));
|
||||
assertEquals("field8", storage.getObject(8));
|
||||
assertEquals(date, storage.getObject(9));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue