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 eba1637454cf1cfa25903f8ab6eb5dda14da3750
Author: Mark Struberg <strub...@apache.org>
AuthorDate: Tue Jul 18 10:08:49 2023 +0200

    OPENJPA-2911 move more BCClass usage to ASM
---
 .../org/apache/openjpa/enhance/PCEnhancer.java     | 34 ++++++---
 .../enhance/asm/EnhancementClassLoader.java        | 76 +++++++++++++++++++
 .../openjpa/enhance/asm/EnhancementProject.java    | 85 ++++++++++++++++++++++
 .../org/apache/openjpa/util/asm/AsmHelper.java     | 12 ++-
 .../openjpa/util/asm/RedefinedAttribute.java       | 46 ++++++++++++
 5 files changed, 237 insertions(+), 16 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 29f0d0c8a..1b12b0828 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
@@ -100,6 +100,8 @@ import org.apache.openjpa.util.StringId;
 import org.apache.openjpa.util.UserException;
 import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.openjpa.util.asm.RedefinedAttribute;
+import org.apache.xbean.asm9.Attribute;
 import org.apache.xbean.asm9.Opcodes;
 import org.apache.xbean.asm9.Type;
 import org.apache.xbean.asm9.tree.*;
@@ -132,6 +134,7 @@ public class PCEnhancer {
     public static final String ISDETACHEDSTATEDEFINITIVE = PRE + 
"isDetachedStateDefinitive";
 
     private static final Class<?> PCTYPE = PersistenceCapable.class;
+    private static final Type TYPE_PCTYPE = 
Type.getType(PersistenceCapable.class);
     private static final String SM = PRE + "StateManager";
     private static final Class<?> SMTYPE = StateManager.class;
     private static final String INHERIT = PRE + "InheritedFieldCount";
@@ -146,7 +149,6 @@ public class PCEnhancer {
     private static final String VERSION_INIT_STR = PRE + "VersionInit";
 
     private static final Localizer _loc = 
Localizer.forPackage(PCEnhancer.class);
-    private static final String REDEFINED_ATTRIBUTE = 
PCEnhancer.class.getName() + "#redefined-type";
 
     private static final AuxiliaryEnhancer[] _auxEnhancers;
 
@@ -536,30 +538,32 @@ public class PCEnhancer {
      * @return <code>ENHANCE_*</code> constant
      */
     public int run() {
-        Class<?> type = _managedType.getType();
         try {
             // if enum, skip, no need of any meta
-            if (type.isEnum())
+            if ((managedType.getClassNode().access & Opcodes.ACC_ENUM) > 0) {
                 return ENHANCE_NONE;
+            }
 
             // if managed interface, skip
-            if (type.isInterface())
+            if ((managedType.getClassNode().access & Opcodes.ACC_INTERFACE) > 
0) {
                 return ENHANCE_INTERFACE;
+            }
 
             // check if already enhanced
             // we cannot simply use instanceof or isAssignableFrom as we have 
a temp ClassLoader inbetween
-            ClassLoader loader = 
AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(type));
-            for (String iface : _managedType.getDeclaredInterfaceNames()) {
-                if (iface.equals(PCTYPE.getName())) {
+            ClassLoader loader = managedType.getClassLoader();
+            for (String iface : managedType.getClassNode().interfaces) {
+                final String pctypeInternalName = 
TYPE_PCTYPE.getInternalName();
+                if (iface.equals(pctypeInternalName)) {
                     if (_log.isTraceEnabled()) {
-                        _log.trace(_loc.get("pc-type", type, loader));
+                        _log.trace(_loc.get("pc-type", 
managedType.getClassNode().name, loader));
                     }
                     return ENHANCE_NONE;
                 }
             }
 
             if (_log.isTraceEnabled()) {
-                _log.trace(_loc.get("enhance-start", type));
+                _log.trace(_loc.get("enhance-start", 
managedType.getClassNode().name));
             }
 
 
@@ -597,15 +601,21 @@ public class PCEnhancer {
         }
         catch (Exception e) {
             throw new GeneralException(_loc.get("enhance-error",
-                                                type.getName(), 
e.getMessage()), e);
+                                                
managedType.getClassNode().name, e.getMessage()), e);
         }
     }
 
     private void configureBCs() {
         if (!_bcsConfigured) {
             if (getRedefine()) {
-                if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null) {
-                    _managedType.addAttribute(REDEFINED_ATTRIBUTE);
+                final boolean isRedefined = managedType.getClassNode().attrs 
!= null &&
+                        managedType.getClassNode().attrs.stream().anyMatch(a 
-> a.isUnknown() && a.type.equals(RedefinedAttribute.ATTR_TYPE));
+
+                if (!isRedefined) {
+                    if (managedType.getClassNode().attrs == null) {
+                        managedType.getClassNode().attrs = new ArrayList<>();
+                    }
+                    managedType.getClassNode().attrs.add(new 
RedefinedAttribute());
                 }
                 else {
                     _isAlreadyRedefined = true;
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementClassLoader.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementClassLoader.java
new file mode 100644
index 000000000..8fac1c4f6
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementClassLoader.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.enhance.asm;
+
+import org.apache.openjpa.util.asm.AsmHelper;
+import org.apache.openjpa.util.asm.ClassNodeTracker;
+
+
+/**
+ * A special ClassLoader to handle classes currently under bytecode 
enhancement.
+ * Inspired by the Serp BCClassLoader, but for ASM based enhancement.
+ *
+ * @author <a href="mailto:strub...@apache.org";>Mark Struberg</a>
+ * @author: Abe White
+ */
+public class EnhancementClassLoader extends ClassLoader {
+
+    private final EnhancementProject project;
+
+    public EnhancementClassLoader(EnhancementProject project) {
+        this.project = project;
+    }
+
+    public EnhancementClassLoader(ClassLoader parent, EnhancementProject 
project) {
+        super(parent);
+        this.project = project;
+    }
+
+    public EnhancementProject getProject() {
+        return project;
+    }
+
+
+    protected Class findClass(String name) throws ClassNotFoundException {
+        byte[] bytes;
+        try {
+            ClassNodeTracker type;
+            if (!project.containsClass(name)) {
+                type = createClass(name);
+            }
+            else {
+                type = project.loadClass(name);
+            }
+            if (type == null) {
+                throw new ClassNotFoundException(name);
+            }
+
+            bytes = AsmHelper.toByteArray(type);
+        } catch (RuntimeException re) {
+            throw new ClassNotFoundException(re.toString());
+        }
+        return defineClass(name, bytes, 0, bytes.length);
+    }
+
+    /**
+     * Override this method if unfound classes should be created on-the-fly.
+     * Returns null by default.
+     */
+    protected ClassNodeTracker createClass(String name) {
+        return null;
+    }
+}
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementProject.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementProject.java
new file mode 100644
index 000000000..43edfc21f
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementProject.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.enhance.asm;
+
+import java.util.HashMap;
+
+import org.apache.openjpa.util.asm.AsmHelper;
+import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.xbean.asm9.tree.ClassNode;
+
+
+/**
+ * Keep track of classes under enhancement.
+ *
+ * @author <a href="mailto:strub...@apache.org";>Mark Struberg</a>
+ */
+public class EnhancementProject {
+
+    private HashMap<String, ClassNodeTracker> classNodTrackers = new 
HashMap<>();
+
+
+    /**
+     * Return true if the project already contains the given class.
+     */
+    public boolean containsClass(String type) {
+        return classNodTrackers.containsKey(type);
+    }
+
+
+    /**
+     * Load a class with the given name.
+     *
+     * @see #loadClass(String,ClassLoader)
+     */
+    public ClassNodeTracker loadClass(String name) {
+        return loadClass(name, null);
+    }
+
+    /**
+     * Load the bytecode for the class with the given name.
+     * If a {@link ClassNodeTracker} with the given name already exists in 
this project,
+     * it will be returned. Otherwise, a new {@link ClassNodeTracker} will be 
created
+     * with the given name and returned. If the name represents an existing
+     * type, the returned instance will contain the parsed bytecode for
+     * that type. If the name is of a primitive or array type, the returned
+     * instance will act accordingly.
+     *
+     * @param name the name of the class, including package
+     * @param loader the class loader to use to search for an existing
+     * class with the given name; if null defaults to the
+     * context loader of the current thread
+     * @throws RuntimeException on parse error
+     */
+    public ClassNodeTracker loadClass(String name, ClassLoader loader) {
+        ClassNodeTracker cached = classNodTrackers.get(name);
+        if (cached != null) {
+            return cached;
+        }
+
+        // check for existing type
+        if (loader == null) {
+            loader = Thread.currentThread().getContextClassLoader();
+        }
+
+        final ClassNode classNode = AsmHelper.readClassNode(loader, name);
+        ClassNodeTracker cnt = new ClassNodeTracker(classNode, loader);
+        classNodTrackers.put(name, cnt);
+        return cnt;
+    }
+
+}
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
index 86e48a868..8bfcd10a0 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
@@ -27,6 +27,7 @@ import java.util.Arrays;
 import java.util.Optional;
 
 import org.apache.openjpa.enhance.asm.BCClassWriter;
+import org.apache.xbean.asm9.Attribute;
 import org.apache.xbean.asm9.ClassReader;
 import org.apache.xbean.asm9.ClassWriter;
 import org.apache.xbean.asm9.Opcodes;
@@ -50,6 +51,9 @@ import serp.bytecode.Project;
  */
 public final class AsmHelper {
     private static final char[] PRIMITIVE_DESCRIPTORS = 
{'V','Z','C','B','S','I','F','J','D'};
+    private static final Attribute[] ATTRS = new Attribute[] {
+            new RedefinedAttribute()
+    };
 
     private AsmHelper() {
         // utility class ct
@@ -67,7 +71,7 @@ public final class AsmHelper {
         try (InputStream in = clazz.getResourceAsStream(className + ".class")) 
{
             ClassReader cr = new ClassReader(in);
             ClassNode classNode = new ClassNode();
-            cr.accept(classNode, 0);
+            cr.accept(classNode, ATTRS, 0);
 
             return classNode;
         }
@@ -92,7 +96,7 @@ public final class AsmHelper {
             throw new RuntimeException(e);
         }
         ClassNode classNode = new ClassNode();
-        cr.accept(classNode, 0);
+        cr.accept(classNode, ATTRS, 0);
 
         return classNode;
     }
@@ -104,7 +108,7 @@ public final class AsmHelper {
     public static ClassWriterTracker toClassWriter(BCClass bcClass) {
         ClassReader cr = new ClassReader(bcClass.toByteArray());
         ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, 
bcClass.getClassLoader());
-        cr.accept(cw, 0);  // 0 -> don't skip anything
+        cr.accept(cw, ATTRS, 0);  // 0 -> don't skip anything
         ClassWriterTracker cwt = new ClassWriterTracker(cw, 
bcClass.getClassLoader());
         cwt.setName(bcClass.getName());
 
@@ -164,7 +168,7 @@ public final class AsmHelper {
     public static ClassNodeTracker toClassNode(BCClass bcClass) {
         ClassReader cr = new ClassReader(bcClass.toByteArray());
         ClassNode classNode = new ClassNode(Opcodes.ASM9);
-        cr.accept(classNode, 0);
+        cr.accept(classNode, ATTRS, 0);
 
         if ((classNode.version & 0xffff) < 49) {
             classNode.version = 49;
diff --git 
a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/RedefinedAttribute.java
 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/RedefinedAttribute.java
new file mode 100644
index 000000000..4fd3265a8
--- /dev/null
+++ 
b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/RedefinedAttribute.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.util.asm;
+
+import org.apache.xbean.asm9.Attribute;
+import org.apache.xbean.asm9.ByteVector;
+import org.apache.xbean.asm9.ClassReader;
+import org.apache.xbean.asm9.ClassWriter;
+import org.apache.xbean.asm9.Label;
+
+/**
+ * Custom Attribute to mark that this class already got redefined.
+ *
+ * @author <a href="mailto:strub...@apache.org";>Mark Struberg</a>
+ */
+public class RedefinedAttribute extends Attribute {
+    public static final String ATTR_TYPE = "org/apache/openjpa/Redefined";
+    public RedefinedAttribute() {
+        super(ATTR_TYPE);
+    }
+
+    @Override
+    protected Attribute read(ClassReader classReader, int offset, int length, 
char[] charBuffer, int codeAttributeOffset, Label[] labels) {
+        return new RedefinedAttribute();
+    }
+
+    @Override
+    protected ByteVector write(ClassWriter classWriter, byte[] code, int 
codeLength, int maxStack, int maxLocals) {
+        int idx = classWriter.newUTF8("Redefined");
+        return new ByteVector().putShort(idx);
+    }
+}

Reply via email to