This is an automated email from the ASF dual-hosted git repository.

struberg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openjpa.git

commit 4c7c81d24958486624ade83a1cc104b34fe51949
Author: Mark Struberg <strub...@apache.org>
AuthorDate: Tue Jun 6 12:11:59 2023 +0200

    OPENJPA-2911 clearFields method via ASM
---
 .../org/apache/openjpa/enhance/PCEnhancer.java     | 257 +++++++++++++++++----
 1 file changed, 217 insertions(+), 40 deletions(-)

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 e00c181ac..bc46dea90 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
@@ -50,6 +50,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 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.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.LdcInsnNode;
 import org.apache.xbean.asm9.tree.MethodInsnNode;
@@ -583,15 +585,17 @@ public class PCEnhancer {
             processViolations();
 
             if (_meta != null) {
-                final ClassNodeTracker classNodeTracker = 
AsmHelper.toClassNode(_pc);
+                ClassNodeTracker classNodeTracker = AsmHelper.toClassNode(_pc);
 
                 enhanceClass(classNodeTracker);
                 addFields(classNodeTracker);
-
+                //X TODO finish addStaticInitializer(classNodeTracker);
                 AsmHelper.readIntoBCClass(classNodeTracker, _pc);
-
                 addStaticInitializer();
-                addPCMethods();
+
+                classNodeTracker = AsmHelper.toClassNode(_pc);
+                addPCMethods(classNodeTracker);
+
                 addAccessors();
                 addAttachDetachCode();
                 addSerializationCode();
@@ -1332,9 +1336,11 @@ public class PCEnhancer {
      * <code>pcFetchObjectId</code>, etc are defined only in the
      * least-derived PersistenceCapable type.
      */
-    private void addPCMethods()
-        throws NoSuchMethodException {
-        addClearFieldsMethod();
+    private void addPCMethods(ClassNodeTracker classNodeTracker) throws 
NoSuchMethodException {
+        //X addClearFieldsMethod();
+        addClearFieldsMethod(classNodeTracker.getClassNode());
+        AsmHelper.readIntoBCClass(classNodeTracker, _pc);
+
         addNewInstanceMethod(true);
         addNewInstanceMethod(false);
         addManagedFieldCountMethod();
@@ -1378,62 +1384,60 @@ public class PCEnhancer {
             addGetIDOwningClass();
         }
     }
-
+    
     /**
      * Add a method to clear all persistent fields; we'll call this from
      * the new instance method to ensure that unloaded fields have
      * default values.
      */
-    private void addClearFieldsMethod()
-        throws NoSuchMethodException {
+    private void addClearFieldsMethod(ClassNode classNode) throws 
NoSuchMethodException {
         // protected void pcClearFields ()
-        BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
-            null);
-        method.makeProtected();
-        Code code = method.getCode(true);
+        MethodNode clearFieldMethod = new MethodNode(Opcodes.ACC_PROTECTED,
+                                                     PRE + "ClearFields",
+                                                     
Type.getMethodDescriptor(Type.VOID_TYPE),
+                                                     null, null);
 
-        // super.pcClearFields ()
+        final InsnList instructions = clearFieldMethod.instructions;
         if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
-            code.aload().setThis();
-            code.invokespecial().setMethod(getType(_meta.
-                getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
-                null);
+            instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+            instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
+                                                
Type.getInternalName(getType(_meta.getPCSuperclassMetaData())),
+                                                PRE + "ClearFields",
+                                                
Type.getMethodDescriptor(Type.VOID_TYPE)));
         }
 
         FieldMetaData[] fmds = _meta.getDeclaredFields();
         for (FieldMetaData fmd : fmds) {
-            if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
+            if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT) {
                 continue;
-
-            loadManagedInstance(code, false);
+            }
+            instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
             switch (fmd.getDeclaredTypeCode()) {
                 case JavaTypes.BOOLEAN:
                 case JavaTypes.BYTE:
                 case JavaTypes.CHAR:
                 case JavaTypes.INT:
                 case JavaTypes.SHORT:
-                    code.constant().setValue(0);
+                    instructions.add(getSetValueInsns(classNode, fmd, 0));
                     break;
                 case JavaTypes.DOUBLE:
-                    code.constant().setValue(0D);
+                    instructions.add(getSetValueInsns(classNode, fmd, 0D));
                     break;
                 case JavaTypes.FLOAT:
-                    code.constant().setValue(0F);
+                    instructions.add(getSetValueInsns(classNode, fmd, 0F));
                     break;
                 case JavaTypes.LONG:
-                    code.constant().setValue(0L);
+                    instructions.add(getSetValueInsns(classNode, fmd, 0L));
                     break;
                 default:
-                    code.constant().setNull();
+                    instructions.add(getSetValueInsns(classNode, fmd, null));
                     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 {
         }
     }
 
-    /**
-     * 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.
-     */
+    @Deprecated
     private void addStaticInitializer() {
         Code code = getOrCreateClassInitCode(true);
         if (_meta.getPCSuperclass() != null) {
@@ -3175,8 +3175,8 @@ public class PCEnhancer {
             } else {
                 // pcInheritedFieldCount = 
<superClass>.pcGetManagedFieldCount()
                 code.invokestatic().setMethod(getType(_meta.
-                    getPCSuperclassMetaData()).getName(),
-                    PRE + "GetManagedFieldCount", int.class.getName(), null);
+                                                              
getPCSuperclassMetaData()).getName(),
+                                              PRE + "GetManagedFieldCount", 
int.class.getName(), null);
                 code.putstatic().setField(INHERIT, int.class);
             }
 
@@ -3184,7 +3184,7 @@ public class PCEnhancer {
             // this intentionally calls getDescribedType() directly
             // instead of PCEnhancer.getType()
             code.classconstant().setClass(
-                _meta.getPCSuperclassMetaData().getDescribedType());
+                    _meta.getPCSuperclassMetaData().getDescribedType());
             code.putstatic().setField(SUPER, Class.class);
         }
 
@@ -3222,6 +3222,103 @@ public class PCEnhancer {
         }
         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();
+    }
+
+    /**
+     * 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 ());
@@ -3250,6 +3347,15 @@ public class PCEnhancer {
 
         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);
     }
 
     /**
@@ -3755,6 +3861,31 @@ public class PCEnhancer {
      * Helper method to get the code for the class initializer method,
      * 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) {
         BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
         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
      * by <code>fmd</code>. Before this method is called, the data to load will

Reply via email to