mirror of https://github.com/apache/openjpa.git
OPENJPA-2911 clearFields method via ASM
This commit is contained in:
parent
80272a3bf7
commit
4c7c81d249
|
@ -50,6 +50,7 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
@ -104,6 +105,7 @@ import org.apache.xbean.asm9.tree.AbstractInsnNode;
|
||||||
import org.apache.xbean.asm9.tree.ClassNode;
|
import org.apache.xbean.asm9.tree.ClassNode;
|
||||||
import org.apache.xbean.asm9.tree.FieldInsnNode;
|
import org.apache.xbean.asm9.tree.FieldInsnNode;
|
||||||
import org.apache.xbean.asm9.tree.FieldNode;
|
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.InsnNode;
|
||||||
import org.apache.xbean.asm9.tree.LdcInsnNode;
|
import org.apache.xbean.asm9.tree.LdcInsnNode;
|
||||||
import org.apache.xbean.asm9.tree.MethodInsnNode;
|
import org.apache.xbean.asm9.tree.MethodInsnNode;
|
||||||
|
@ -583,15 +585,17 @@ public class PCEnhancer {
|
||||||
processViolations();
|
processViolations();
|
||||||
|
|
||||||
if (_meta != null) {
|
if (_meta != null) {
|
||||||
final ClassNodeTracker classNodeTracker = AsmHelper.toClassNode(_pc);
|
ClassNodeTracker classNodeTracker = AsmHelper.toClassNode(_pc);
|
||||||
|
|
||||||
enhanceClass(classNodeTracker);
|
enhanceClass(classNodeTracker);
|
||||||
addFields(classNodeTracker);
|
addFields(classNodeTracker);
|
||||||
|
//X TODO finish addStaticInitializer(classNodeTracker);
|
||||||
AsmHelper.readIntoBCClass(classNodeTracker, _pc);
|
AsmHelper.readIntoBCClass(classNodeTracker, _pc);
|
||||||
|
|
||||||
addStaticInitializer();
|
addStaticInitializer();
|
||||||
addPCMethods();
|
|
||||||
|
classNodeTracker = AsmHelper.toClassNode(_pc);
|
||||||
|
addPCMethods(classNodeTracker);
|
||||||
|
|
||||||
addAccessors();
|
addAccessors();
|
||||||
addAttachDetachCode();
|
addAttachDetachCode();
|
||||||
addSerializationCode();
|
addSerializationCode();
|
||||||
|
@ -1332,9 +1336,11 @@ public class PCEnhancer {
|
||||||
* <code>pcFetchObjectId</code>, etc are defined only in the
|
* <code>pcFetchObjectId</code>, etc are defined only in the
|
||||||
* least-derived PersistenceCapable type.
|
* least-derived PersistenceCapable type.
|
||||||
*/
|
*/
|
||||||
private void addPCMethods()
|
private void addPCMethods(ClassNodeTracker classNodeTracker) throws NoSuchMethodException {
|
||||||
throws NoSuchMethodException {
|
//X addClearFieldsMethod();
|
||||||
addClearFieldsMethod();
|
addClearFieldsMethod(classNodeTracker.getClassNode());
|
||||||
|
AsmHelper.readIntoBCClass(classNodeTracker, _pc);
|
||||||
|
|
||||||
addNewInstanceMethod(true);
|
addNewInstanceMethod(true);
|
||||||
addNewInstanceMethod(false);
|
addNewInstanceMethod(false);
|
||||||
addManagedFieldCountMethod();
|
addManagedFieldCountMethod();
|
||||||
|
@ -1384,56 +1390,54 @@ public class PCEnhancer {
|
||||||
* the new instance method to ensure that unloaded fields have
|
* the new instance method to ensure that unloaded fields have
|
||||||
* default values.
|
* default values.
|
||||||
*/
|
*/
|
||||||
private void addClearFieldsMethod()
|
private void addClearFieldsMethod(ClassNode classNode) throws NoSuchMethodException {
|
||||||
throws NoSuchMethodException {
|
|
||||||
// protected void pcClearFields ()
|
// protected void pcClearFields ()
|
||||||
BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
|
MethodNode clearFieldMethod = new MethodNode(Opcodes.ACC_PROTECTED,
|
||||||
null);
|
PRE + "ClearFields",
|
||||||
method.makeProtected();
|
Type.getMethodDescriptor(Type.VOID_TYPE),
|
||||||
Code code = method.getCode(true);
|
null, null);
|
||||||
|
|
||||||
// super.pcClearFields ()
|
final InsnList instructions = clearFieldMethod.instructions;
|
||||||
if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
|
if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
|
||||||
code.aload().setThis();
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
code.invokespecial().setMethod(getType(_meta.
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||||
getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
|
Type.getInternalName(getType(_meta.getPCSuperclassMetaData())),
|
||||||
null);
|
PRE + "ClearFields",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldMetaData[] fmds = _meta.getDeclaredFields();
|
FieldMetaData[] fmds = _meta.getDeclaredFields();
|
||||||
for (FieldMetaData fmd : fmds) {
|
for (FieldMetaData fmd : fmds) {
|
||||||
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
|
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
loadManagedInstance(code, false);
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
switch (fmd.getDeclaredTypeCode()) {
|
switch (fmd.getDeclaredTypeCode()) {
|
||||||
case JavaTypes.BOOLEAN:
|
case JavaTypes.BOOLEAN:
|
||||||
case JavaTypes.BYTE:
|
case JavaTypes.BYTE:
|
||||||
case JavaTypes.CHAR:
|
case JavaTypes.CHAR:
|
||||||
case JavaTypes.INT:
|
case JavaTypes.INT:
|
||||||
case JavaTypes.SHORT:
|
case JavaTypes.SHORT:
|
||||||
code.constant().setValue(0);
|
instructions.add(getSetValueInsns(classNode, fmd, 0));
|
||||||
break;
|
break;
|
||||||
case JavaTypes.DOUBLE:
|
case JavaTypes.DOUBLE:
|
||||||
code.constant().setValue(0D);
|
instructions.add(getSetValueInsns(classNode, fmd, 0D));
|
||||||
break;
|
break;
|
||||||
case JavaTypes.FLOAT:
|
case JavaTypes.FLOAT:
|
||||||
code.constant().setValue(0F);
|
instructions.add(getSetValueInsns(classNode, fmd, 0F));
|
||||||
break;
|
break;
|
||||||
case JavaTypes.LONG:
|
case JavaTypes.LONG:
|
||||||
code.constant().setValue(0L);
|
instructions.add(getSetValueInsns(classNode, fmd, 0L));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
code.constant().setNull();
|
instructions.add(getSetValueInsns(classNode, fmd, null));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
addSetManagedValueCode(code, fmd);
|
|
||||||
}
|
}
|
||||||
|
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
|
||||||
|
classNode.methods.add(clearFieldMethod);
|
||||||
|
|
||||||
code.vreturn();
|
|
||||||
code.calculateMaxStack();
|
|
||||||
code.calculateMaxLocals();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3161,11 +3165,7 @@ public class PCEnhancer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Modifies the class initialization method (creating one if necessary)
|
|
||||||
* to initialize the static fields of the PersistenceCapable instance and
|
|
||||||
* to register it with the impl helper.
|
|
||||||
*/
|
|
||||||
private void addStaticInitializer() {
|
private void addStaticInitializer() {
|
||||||
Code code = getOrCreateClassInitCode(true);
|
Code code = getOrCreateClassInitCode(true);
|
||||||
if (_meta.getPCSuperclass() != null) {
|
if (_meta.getPCSuperclass() != null) {
|
||||||
|
@ -3252,6 +3252,112 @@ public class PCEnhancer {
|
||||||
code.calculateMaxStack();
|
code.calculateMaxStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the class initialization method (creating one if necessary)
|
||||||
|
* to initialize the static fields of the PersistenceCapable instance and
|
||||||
|
* to register it with the impl helper.
|
||||||
|
*/
|
||||||
|
private void addStaticInitializer(ClassNodeTracker classNodeTracker) {
|
||||||
|
final ClassNode classNode = classNodeTracker.getClassNode();
|
||||||
|
InsnList instructions = new InsnList();
|
||||||
|
if (_meta.getPCSuperclass() != null) {
|
||||||
|
if (getCreateSubclass()) {
|
||||||
|
instructions.add(new LdcInsnNode(0));
|
||||||
|
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, INHERIT, Type.getDescriptor(int.class)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// pcInheritedFieldCount = <superClass>.pcGetManagedFieldCount()
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, classNode.superName,
|
||||||
|
PRE + "GetManagedFieldCount",
|
||||||
|
Type.getMethodDescriptor(Type.INT_TYPE)));
|
||||||
|
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, INHERIT, Type.getDescriptor(int.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcPCSuperclass = <superClass>;
|
||||||
|
// this intentionally calls getDescribedType() directly
|
||||||
|
// instead of PCEnhancer.getType()
|
||||||
|
instructions.add(new LdcInsnNode(Type.getInternalName(_meta.getPCSuperclassMetaData().getDescribedType())));
|
||||||
|
instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, SUPER, Type.getDescriptor(Class.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcFieldNames = new String[] { "<name1>", "<name2>", ... };
|
||||||
|
FieldMetaData[] fmds = _meta.getDeclaredFields();
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// pcFieldNames = new String[] { "<name1>", "<name2>", ... };
|
||||||
|
FieldMetaData[] fmds = _meta.getDeclaredFields();
|
||||||
|
code.constant().setValue(fmds.length);
|
||||||
|
code.anewarray().setType(String.class);
|
||||||
|
for (int i = 0; i < fmds.length; i++) {
|
||||||
|
code.dup();
|
||||||
|
code.constant().setValue(i);
|
||||||
|
code.constant().setValue(fmds[i].getName());
|
||||||
|
code.aastore();
|
||||||
|
}
|
||||||
|
code.putstatic().setField(PRE + "FieldNames", String[].class);
|
||||||
|
|
||||||
|
// pcFieldTypes = new Class[] { <type1>.class, <type2>.class, ... };
|
||||||
|
code.constant().setValue(fmds.length);
|
||||||
|
code.anewarray().setType(Class.class);
|
||||||
|
for (int i = 0; i < fmds.length; i++) {
|
||||||
|
code.dup();
|
||||||
|
code.constant().setValue(i);
|
||||||
|
code.classconstant().setClass(fmds[i].getDeclaredType());
|
||||||
|
code.aastore();
|
||||||
|
}
|
||||||
|
code.putstatic().setField(PRE + "FieldTypes", Class[].class);
|
||||||
|
|
||||||
|
// pcFieldFlags = new byte[] { <flag1>, <flag2>, ... };
|
||||||
|
code.constant().setValue(fmds.length);
|
||||||
|
code.newarray().setType(byte.class);
|
||||||
|
for (int i = 0; i < fmds.length; i++) {
|
||||||
|
code.dup();
|
||||||
|
code.constant().setValue(i);
|
||||||
|
code.constant().setValue(getFieldFlag(fmds[i]));
|
||||||
|
code.bastore();
|
||||||
|
}
|
||||||
|
code.putstatic().setField(PRE + "FieldFlags", byte[].class);
|
||||||
|
|
||||||
|
// PCRegistry.register (cls,
|
||||||
|
// pcFieldNames, pcFieldTypes, pcFieldFlags,
|
||||||
|
// pcPCSuperclass, alias, new XXX ());
|
||||||
|
code.classconstant().setClass(_meta.getDescribedType());
|
||||||
|
code.getstatic().setField(PRE + "FieldNames", String[].class);
|
||||||
|
code.getstatic().setField(PRE + "FieldTypes", Class[].class);
|
||||||
|
code.getstatic().setField(PRE + "FieldFlags", byte[].class);
|
||||||
|
code.getstatic().setField(SUPER, Class.class);
|
||||||
|
|
||||||
|
if (_meta.isMapped() || _meta.isAbstract())
|
||||||
|
code.constant().setValue(_meta.getTypeAlias());
|
||||||
|
else
|
||||||
|
code.constant().setNull();
|
||||||
|
|
||||||
|
if (_pc.isAbstract())
|
||||||
|
code.constant().setNull();
|
||||||
|
else {
|
||||||
|
code.anew().setType(_pc);
|
||||||
|
code.dup();
|
||||||
|
code.invokespecial().setMethod("<init>", void.class, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.invokestatic().setMethod(HELPERTYPE, "register", void.class,
|
||||||
|
new Class[]{ Class.class, String[].class, Class[].class,
|
||||||
|
byte[].class, Class.class, String.class, PCTYPE });
|
||||||
|
|
||||||
|
code.vreturn();
|
||||||
|
code.calculateMaxStack();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// now add those instructions to the <clinit> method
|
||||||
|
MethodNode clinit = getOrCreateClassInitMethod(classNode);
|
||||||
|
final AbstractInsnNode retInsn = clinit.instructions.getLast();
|
||||||
|
if (retInsn.getOpcode() != Opcodes.RETURN) {
|
||||||
|
throw new IllegalStateException("Problem with parsing instructions. RETURN expected");
|
||||||
|
}
|
||||||
|
clinit.instructions.insertBefore(retInsn, instructions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the flag for the given field.
|
* Return the flag for the given field.
|
||||||
*/
|
*/
|
||||||
|
@ -3755,6 +3861,31 @@ public class PCEnhancer {
|
||||||
* Helper method to get the code for the class initializer method,
|
* Helper method to get the code for the class initializer method,
|
||||||
* creating the method if it does not already exist.
|
* creating the method if it does not already exist.
|
||||||
*/
|
*/
|
||||||
|
private MethodNode getOrCreateClassInitMethod(ClassNode classNode) {
|
||||||
|
final Optional<MethodNode> clinitMethodNode = classNode.methods.stream()
|
||||||
|
.filter(m -> m.name.equals("<clinit>"))
|
||||||
|
.findFirst();
|
||||||
|
if (clinitMethodNode.isPresent()) {
|
||||||
|
|
||||||
|
return clinitMethodNode.get();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// add static initializer method if non exists
|
||||||
|
MethodNode clinit = new MethodNode(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
|
||||||
|
"<clinit>",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE),
|
||||||
|
null, null);
|
||||||
|
clinit.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
classNode.methods.add(clinit);
|
||||||
|
return clinit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get the code for the class initializer method,
|
||||||
|
* creating the method if it does not already exist.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
private Code getOrCreateClassInitCode(boolean replaceLast) {
|
private Code getOrCreateClassInitCode(boolean replaceLast) {
|
||||||
BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
|
BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
|
||||||
Code code;
|
Code code;
|
||||||
|
@ -4767,6 +4898,52 @@ public class PCEnhancer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given value into the field value specified
|
||||||
|
* by <code>fmd</code>. Before this method is called, the data to load will
|
||||||
|
* be on the top of the stack and the object that the data should be loaded
|
||||||
|
* into will be second in the stack.
|
||||||
|
*/
|
||||||
|
private InsnList getSetValueInsns(ClassNode classNode, FieldMetaData fmd, Object value) {
|
||||||
|
InsnList instructions = new InsnList();
|
||||||
|
if (value == null) {
|
||||||
|
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instructions.add(new LdcInsnNode(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if redefining, then we must always reflect (or access the field
|
||||||
|
// directly if accessible), since the redefined methods will always
|
||||||
|
// trigger method calls to StateManager, even from internal direct-
|
||||||
|
// access usage. We could work around this by not redefining, and
|
||||||
|
// just do a subclass approach instead. But this is not a good option,
|
||||||
|
// since it would sacrifice lazy loading and efficient dirty tracking.
|
||||||
|
if (getRedefine() || isFieldAccess(fmd)) {
|
||||||
|
instructions.add(new FieldInsnNode(Opcodes.PUTFIELD,
|
||||||
|
Type.getInternalName(fmd.getDeclaringType()),
|
||||||
|
fmd.getName(),
|
||||||
|
Type.getDescriptor(fmd.getDeclaredType())));
|
||||||
|
}
|
||||||
|
else if (getCreateSubclass()) {
|
||||||
|
// property access, and we're not redefining. invoke the
|
||||||
|
// superclass method to bypass tracking.
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||||
|
Type.getInternalName(_managedType.getType()),
|
||||||
|
getSetterName(fmd),
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(fmd.getDeclaredType()))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// regular enhancement + property access
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
classNode.name,
|
||||||
|
PRE + getSetterName(fmd),
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(fmd.getDeclaredType()))));
|
||||||
|
|
||||||
|
}
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the value at the top of the stack into the field value specified
|
* Store the value at the top of the stack into the field value specified
|
||||||
* by <code>fmd</code>. Before this method is called, the data to load will
|
* by <code>fmd</code>. Before this method is called, the data to load will
|
||||||
|
|
Loading…
Reference in New Issue