You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2023/07/18 08:09:36 UTC

[openjpa] branch master updated (86c266df7 -> eba163745)

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

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


    from 86c266df7 OPENJPA-2911 openjpa-lib without Serp
     new eabceb69f OPENJPA-2911 inline ASM adapter handling
     new eba163745 OPENJPA-2911 move more BCClass usage to ASM

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/openjpa/enhance/AsmAdaptor.java     |  79 -------
 .../openjpa/enhance/PCClassFileTransformer.java    |  45 +++-
 .../org/apache/openjpa/enhance/PCEnhancer.java     |  34 ++-
 .../org/apache/openjpa/enhance/asm/AsmSpi.java     |  42 ----
 .../org/apache/openjpa/enhance/asm/AsmSpi9.java    | 185 ----------------
 .../enhance/asm/EnhancementClassLoader.java        |  76 +++++++
 .../openjpa/enhance/asm/EnhancementProject.java    |  85 +++++++
 .../org/apache/openjpa/util/asm/AsmHelper.java     |  39 +++-
 ...assNodeTracker.java => RedefinedAttribute.java} |  30 +--
 .../org/apache/openjpa/enhance/TestAsmAdaptor.java | 243 ---------------------
 10 files changed, 279 insertions(+), 579 deletions(-)
 delete mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java
 delete mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java
 delete mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java
 create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementClassLoader.java
 create mode 100644 openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/EnhancementProject.java
 copy openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/{ClassNodeTracker.java => RedefinedAttribute.java} (51%)
 delete mode 100644 openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java


[openjpa] 01/02: OPENJPA-2911 inline ASM adapter handling

Posted by st...@apache.org.
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 eabceb69f43f28e2ac2698bc40e1044dd3f80331
Author: Mark Struberg <st...@apache.org>
AuthorDate: Mon Jul 17 23:01:16 2023 +0200

    OPENJPA-2911 inline ASM adapter handling
---
 .../org/apache/openjpa/enhance/AsmAdaptor.java     |  79 -------
 .../openjpa/enhance/PCClassFileTransformer.java    |  45 +++-
 .../org/apache/openjpa/enhance/asm/AsmSpi.java     |  42 ----
 .../org/apache/openjpa/enhance/asm/AsmSpi9.java    | 185 ----------------
 .../org/apache/openjpa/util/asm/AsmHelper.java     |  27 +++
 .../org/apache/openjpa/enhance/TestAsmAdaptor.java | 243 ---------------------
 6 files changed, 71 insertions(+), 550 deletions(-)

diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java
deleted file mode 100644
index 86d28181b..000000000
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/AsmAdaptor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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;
-
-import org.apache.openjpa.enhance.asm.AsmSpi;
-import org.apache.openjpa.enhance.asm.AsmSpi9;
-import serp.bytecode.BCClass;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ServiceLoader;
-import java.util.stream.StreamSupport;
-
-
-/**
- * Use ASM to add required StackMapTable attribute to the byte code generated by
- * Serp.
- */
-public final class AsmAdaptor {
-    private final static AsmSpi impl;
-    static {
-        impl = StreamSupport.stream(ServiceLoader.load(AsmSpi.class).spliterator(), false).min((a, b) -> {
-            final int v1;
-            try {
-                v1 = Integer.parseInt(a.getClass().getName().replace("AsmSpi", ""));
-            } catch (final Exception e) {
-                // not matching our default naming so an user impl so let's use it
-                return -1;
-            }
-            final int v2;
-            try {
-                v2 = Integer.parseInt(b.getClass().getName().replace("AsmSpi", ""));
-            } catch (final Exception e) {
-                // not matching our default naming so an user impl so let's use it
-                return 1;
-            }
-            return v2 - v1; // reverse since we want the higher
-        }).orElseGet(AsmSpi9::new);
-    }
-
-    @SuppressWarnings("deprecation")
-    public static void write(BCClass bc) throws IOException {
-        impl.write(bc);
-    }
-
-    public static void write(BCClass bc, File outFile) throws IOException {
-        impl.write(bc, outFile);
-    }
-
-    public static void write(BCClass bc, OutputStream os) throws IOException {
-        impl.write(bc, os);
-    }
-
-    public static byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException {
-        return impl.toByteArray(bc, returnBytes);
-    }
-
-    public static boolean isEnhanced(final byte[] b)
-    {
-        return impl.isEnhanced(b);
-    }
-}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
index 246083dff..f46663f31 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
@@ -34,8 +34,12 @@ import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.util.GeneralException;
 import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.asm.ClassNodeTracker;
+import org.apache.xbean.asm9.ClassReader;
+import org.apache.xbean.asm9.ClassVisitor;
+import org.apache.xbean.asm9.Opcodes;
 
 import serp.bytecode.Project;
+import static java.util.Arrays.asList;
 
 
 /**
@@ -221,7 +225,46 @@ public class PCClassFileTransformer
      * {@link PersistenceCapable}.
      */
     private static boolean isEnhanced(byte[] b) {
-        return AsmAdaptor.isEnhanced(b);
+        if (b == null)
+        {
+            return false;
+        }
+        final ClassReader cr = new ClassReader(b);
+        try
+        {
+            cr.accept(new ClassVisitor(Opcodes.ASM9)
+            {
+                @Override
+                public void visit(final int i, final int i1,
+                                  final String name, final String s,
+                                  final String parent, final String[] interfaces)
+                {
+                    boolean enhanced = interfaces != null && interfaces.length > 0 &&
+                            asList(interfaces).contains("org/apache/openjpa/enhance/PersistenceCapable");
+                    if (!enhanced && name != null && parent != null &&
+                            !"java/lang/Object".equals(parent) && !name.equals(parent)) {
+                        enhanced = isEnhanced(AsmHelper.getClassBytes(parent));
+                    }
+                    throw new EnhancedStatusException(enhanced);
+                }
+            }, 0);
+            return false;
+        } catch (final EnhancedStatusException e) {
+            return e.status;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+
+
+    private static class EnhancedStatusException extends RuntimeException {
+
+        private static final long serialVersionUID = 1L;
+        private final boolean status;
+
+        private EnhancedStatusException(final boolean status) {
+            this.status = status;
+        }
     }
 
     public static class Reentrant extends PCClassFileTransformer {
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java
deleted file mode 100644
index 26af22ee0..000000000
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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 serp.bytecode.BCClass;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * Enables to abstract ASM usage for the runtime (not for tests).
- *
- * It is recommended to name the impl "AsmSpi{asmVersion}" to let the spi loader sort the impl properly.
- */
-public interface AsmSpi {
-    void write(BCClass bc) throws IOException;
-
-    void write(BCClass bc, File outFile) throws IOException;
-
-    void write(BCClass bc, OutputStream os) throws IOException;
-
-    byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException;
-
-    boolean isEnhanced(byte[] b);
-}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java
deleted file mode 100644
index a3e887b27..000000000
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/asm/AsmSpi9.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.xbean.asm9.ClassReader;
-import org.apache.xbean.asm9.ClassVisitor;
-import org.apache.xbean.asm9.ClassWriter;
-import org.apache.xbean.asm9.Opcodes;
-import serp.bytecode.BCClass;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URLDecoder;
-
-import static java.util.Arrays.asList;
-
-public class AsmSpi9 implements AsmSpi {
-    private static final int Java7_MajorVersion = 51;
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public void write(BCClass bc) throws IOException {
-        if (bc.getMajorVersion() < Java7_MajorVersion) {
-            bc.write();
-        } else {
-            String name = bc.getName();
-            int dotIndex = name.lastIndexOf('.') + 1;
-            name = name.substring(dotIndex);
-            Class<?> type = bc.getType();
-
-            OutputStream out = new FileOutputStream(
-                    URLDecoder.decode(type.getResource(name + ".class").getFile()));
-            try {
-                writeJava7(bc, out);
-            } finally {
-                out.flush();
-                out.close();
-            }
-        }
-    }
-
-    @Override
-    public void write(BCClass bc, File outFile) throws IOException {
-        if (bc.getMajorVersion() < Java7_MajorVersion) {
-            bc.write(outFile);
-        } else {
-            OutputStream out = new FileOutputStream(outFile);
-            try {
-                writeJava7(bc, out);
-            } finally {
-                out.flush();
-                out.close();
-            }
-        }
-    }
-
-    @Override
-    public void write(BCClass bc, OutputStream os) throws IOException {
-        if (bc.getMajorVersion() < Java7_MajorVersion) {
-            bc.write(os);
-        }
-        else {
-            try {
-                writeJava7(bc, os);
-            } finally {
-                os.flush();
-                os.close();
-            }
-        }
-    }
-
-    @Override
-    public byte[] toByteArray(BCClass bc, byte[] returnBytes) throws IOException {
-        if (bc.getMajorVersion() >= Java7_MajorVersion) {
-            returnBytes = toJava7ByteArray(bc, returnBytes);
-        }
-        return returnBytes;
-    }
-
-    private void writeJava7(BCClass bc, OutputStream out) throws IOException {
-        byte[] java7Bytes = toJava7ByteArray(bc, bc.toByteArray());
-        out.write(java7Bytes);
-    }
-
-    private byte[] toJava7ByteArray(BCClass bc, byte[] classBytes) throws IOException {
-        ByteArrayInputStream bais = new ByteArrayInputStream(classBytes);
-        BufferedInputStream bis = new BufferedInputStream(bais);
-
-        ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, bc.getClassLoader());
-        ClassReader cr = new ClassReader(bis);
-        cr.accept(cw, 0);
-        return cw.toByteArray();
-    }
-
-    public boolean isEnhanced(final byte[] b)
-    {
-        if (b == null)
-        {
-            return false;
-        }
-        final ClassReader cr = new ClassReader(b);
-        try
-        {
-            cr.accept(new ClassVisitor(Opcodes.ASM8)
-            {
-                @Override
-                public void visit(final int i, final int i1,
-                                  final String name, final String s,
-                                  final String parent, final String[] interfaces)
-                {
-                    boolean enhanced = interfaces != null && interfaces.length > 0 &&
-                            asList(interfaces).contains("org/apache/openjpa/enhance/PersistenceCapable");
-                    if (!enhanced && name != null && parent != null &&
-                            !"java/lang/Object".equals(parent) && !name.equals(parent)) {
-                        enhanced = isEnhanced(bytes(parent));
-                    }
-                    throw new EnhancedStatusException(enhanced);
-                }
-            }, 0);
-            return false;
-        } catch (final EnhancedStatusException e) {
-            return e.status;
-        } catch (final Exception e) {
-            return false;
-        }
-    }
-
-    private byte[] bytes(final String type)
-    {
-        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
-        final InputStream stream = Thread.currentThread().getContextClassLoader()
-                .getResourceAsStream(type + ".class");
-        if (stream == null) {
-            return null;
-        }
-        try {
-            int c;
-            byte[] buffer = new byte[1024];
-            while ((c = stream.read(buffer)) >= 0) {
-                baos.write(buffer, 0, c);
-            }
-        } catch (IOException e) {
-            return null;
-        } finally {
-            try {
-                stream.close();
-            } catch (IOException e) {
-                // no-op
-            }
-        }
-        return baos.toByteArray();
-    }
-
-    private static class EnhancedStatusException extends RuntimeException {
-
-        private static final long serialVersionUID = 1L;
-        private final boolean status;
-
-        private EnhancedStatusException(final boolean status) {
-            this.status = status;
-        }
-    }
-}
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 d86bb6b85..86e48a868 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
@@ -17,6 +17,7 @@
 package org.apache.openjpa.util.asm;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Array;
@@ -110,6 +111,32 @@ public final class AsmHelper {
         return cwt;
     }
 
+
+    public static byte[] getClassBytes(final String typeName)
+    {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+        final InputStream stream = Thread.currentThread().getContextClassLoader()
+                .getResourceAsStream(typeName + ".class");
+        if (stream == null) {
+            return null;
+        }
+        try {
+            int c;
+            byte[] buffer = new byte[1024];
+            while ((c = stream.read(buffer)) >= 0) {
+                baos.write(buffer, 0, c);
+            }
+        } catch (IOException e) {
+            return null;
+        } finally {
+            try {
+                stream.close();
+            } catch (IOException e) {
+                // no-op
+            }
+        }
+        return baos.toByteArray();
+    }
     /**
      * Create a byte[] of that class represented by the ClassNodeTracker
      */
diff --git a/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java b/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java
deleted file mode 100644
index ebe8be60b..000000000
--- a/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestAsmAdaptor.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.openjpa.util.QueryException;
-import org.apache.openjpa.util.asm.AsmHelper;
-import org.apache.xbean.asm9.tree.ClassNode;
-import org.junit.Test;
-
-public class TestAsmAdaptor
-{
-    @Test
-    public void testAsmHelper() throws Exception {
-        final ClassNode classNode = AsmHelper.readClassNode(QueryException.class);
-        assertNotNull(classNode);
-    }
-    @Test
-    public void isEnhanced()
-    {
-        assertTrue(AsmAdaptor.isEnhanced(bytes(Enhanced.class)));
-        assertTrue(AsmAdaptor.isEnhanced(bytes(TransitivelyEnhanced.class)));
-        assertFalse(AsmAdaptor.isEnhanced(bytes(NotEnhanced.class)));
-    }
-
-    private byte[] bytes(final Class<?> type)
-    {
-        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
-        try (InputStream stream = Thread.currentThread().getContextClassLoader()
-                .getResourceAsStream(type.getName().replace('.', '/') + ".class")) {
-            int c;
-            byte[] buffer = new byte[1024];
-            while ((c = stream.read(buffer)) >= 0) {
-                baos.write(buffer, 0, c);
-            }
-        }
-        catch (IOException e) {
-            fail(e.getMessage());
-        }
-        // no-op
-        return baos.toByteArray();
-    }
-
-    public static class NotEnhanced
-    {
-    }
-
-    public static class TransitivelyEnhanced extends Enhanced
-    {
-    }
-
-    public static class Enhanced implements PersistenceCapable // just a mock for the test
-    {
-        @Override
-        public int pcGetEnhancementContractVersion()
-        {
-            return 0;
-        }
-
-        @Override
-        public Object pcGetGenericContext()
-        {
-            return null;
-        }
-
-        @Override
-        public StateManager pcGetStateManager()
-        {
-            return null;
-        }
-
-        @Override
-        public void pcReplaceStateManager(StateManager sm)
-        {
-
-        }
-
-        @Override
-        public void pcProvideField(int fieldIndex)
-        {
-
-        }
-
-        @Override
-        public void pcProvideFields(int[] fieldIndices)
-        {
-
-        }
-
-        @Override
-        public void pcReplaceField(int fieldIndex)
-        {
-
-        }
-
-        @Override
-        public void pcReplaceFields(int[] fieldIndex)
-        {
-
-        }
-
-        @Override
-        public void pcCopyFields(Object fromObject, int[] fields)
-        {
-
-        }
-
-        @Override
-        public void pcDirty(String fieldName)
-        {
-
-        }
-
-        @Override
-        public Object pcFetchObjectId()
-        {
-            return null;
-        }
-
-        @Override
-        public Object pcGetVersion()
-        {
-            return null;
-        }
-
-        @Override
-        public boolean pcIsDirty()
-        {
-            return false;
-        }
-
-        @Override
-        public boolean pcIsTransactional()
-        {
-            return false;
-        }
-
-        @Override
-        public boolean pcIsPersistent()
-        {
-            return false;
-        }
-
-        @Override
-        public boolean pcIsNew()
-        {
-            return false;
-        }
-
-        @Override
-        public boolean pcIsDeleted()
-        {
-            return false;
-        }
-
-        @Override
-        public Boolean pcIsDetached()
-        {
-            return null;
-        }
-
-        @Override
-        public PersistenceCapable pcNewInstance(StateManager sm, boolean clear)
-        {
-            return null;
-        }
-
-        @Override
-        public PersistenceCapable pcNewInstance(StateManager sm, Object obj, boolean clear)
-        {
-            return null;
-        }
-
-        @Override
-        public Object pcNewObjectIdInstance()
-        {
-            return null;
-        }
-
-        @Override
-        public Object pcNewObjectIdInstance(Object obj)
-        {
-            return null;
-        }
-
-        @Override
-        public void pcCopyKeyFieldsToObjectId(Object obj)
-        {
-
-        }
-
-        @Override
-        public void pcCopyKeyFieldsToObjectId(FieldSupplier supplier, Object obj)
-        {
-
-        }
-
-        @Override
-        public void pcCopyKeyFieldsFromObjectId(FieldConsumer consumer, Object obj)
-        {
-
-        }
-
-        @Override
-        public Object pcGetDetachedState()
-        {
-            return null;
-        }
-
-        @Override
-        public void pcSetDetachedState(Object state)
-        {
-
-        }
-    }
-}
-
-
-


[openjpa] 02/02: OPENJPA-2911 move more BCClass usage to ASM

Posted by st...@apache.org.
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 <st...@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:struberg@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:struberg@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:struberg@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);
+    }
+}