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 11367cfbeae7e71c22804fc7c1040989388ea5cb Author: Mark Struberg <strub...@apache.org> AuthorDate: Mon Jul 17 18:06:43 2023 +0200 OPENJPA-2911 ByteCodeWriter with ASM --- .../openjpa/enhance/ManagedClassSubclasser.java | 70 ++++++++++++---------- .../org/apache/openjpa/enhance/PCEnhancer.java | 36 +++++------ .../openjpa/util/ClassLoaderProxyService.java | 1 + .../org/apache/openjpa/util/GeneratedClasses.java | 2 +- .../org/apache/openjpa/util/asm/AsmHelper.java | 9 +++ .../apache/openjpa/util/asm/BytecodeWriter.java | 2 +- .../enhance/TestEnhancementWithMultiplePUs.java | 24 ++++---- 7 files changed, 77 insertions(+), 67 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java index 2fc77a346..cde3f0412 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ManagedClassSubclasser.java @@ -18,9 +18,10 @@ */ package org.apache.openjpa.enhance; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,8 +33,8 @@ import java.util.Set; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.lib.log.Log; +import org.apache.openjpa.util.asm.AsmHelper; import org.apache.openjpa.util.asm.BytecodeWriter; -import org.apache.openjpa.lib.util.Files; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Localizer.Message; import org.apache.openjpa.meta.AccessCode; @@ -46,8 +47,7 @@ import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.MetaDataException; import org.apache.openjpa.util.UserException; - -import serp.bytecode.BCClass; +import org.apache.openjpa.util.asm.ClassNodeTracker; /** * Redefines the method bodies of existing unenhanced classes to make them @@ -65,7 +65,7 @@ public class ManagedClassSubclasser { * OpenJPA to handle new instances of the unenhanced type. If this is * invoked in a Java 6 environment, this method will redefine the methods * for each class in the argument list such that field accesses are - * intercepted in-line. If invoked in a Java 5 environment, this + * intercepted in-line. If invoked in a Java 5 environment or very new Java versions, this * redefinition is not possible; in these contexts, when using field * access, OpenJPA will need to do state comparisons to detect any change * to any instance at any time, and when using property access, OpenJPA @@ -136,8 +136,8 @@ public class ManagedClassSubclasser { enhancer.setBytecodeWriter(new BytecodeWriter() { @Override - public void write(BCClass bc) throws IOException { - ManagedClassSubclasser.write(bc, enhancer, map, c, subs, ints); + public void write(ClassNodeTracker cnt) throws IOException { + ManagedClassSubclasser.write(cnt, enhancer, map, c, subs, ints); } }); if (redefine) { @@ -174,9 +174,9 @@ public class ManagedClassSubclasser { } } - if (unspecified != null && !unspecified.isEmpty()) - throw new UserException(_loc.get("unspecified-unenhanced-types", Exceptions.toClassNames(classes), - unspecified)); + if (unspecified != null && !unspecified.isEmpty()) { + throw new UserException(_loc.get("unspecified-unenhanced-types", Exceptions.toClassNames(classes), unspecified)); + } ClassRedefiner.redefineClasses(conf, map); for (Class<?> cls : map.keySet()) { @@ -269,55 +269,61 @@ public class ManagedClassSubclasser { } } - private static void write(BCClass bc, PCEnhancer enhancer, - Map<Class<?>, byte[]> map, Class<?> cls, List<Class<?>> subs, List<Class<?>> ints) + private static void write(ClassNodeTracker cnt, PCEnhancer enhancer, Map<Class<?>, byte[]> map, + Class<?> cls, List<Class<?>> subs, List<Class<?>> ints) throws IOException { - if (bc == enhancer.getManagedTypeBytecode()) { + if (cnt == enhancer.getManagedTypeBytecode()) { // if it was already defined, don't put it in the map, // but do set the metadata accordingly. if (enhancer.isAlreadyRedefined()) - ints.add(bc.getType()); + ints.add(cls); else { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - AsmAdaptor.write(bc, baos); - map.put(bc.getType(), baos.toByteArray()); - debugBytecodes(bc); + final byte[] byteArray = AsmHelper.toByteArray(cnt); + map.put(cls, byteArray); + debugBytecodes(cnt, byteArray); } } else { if (!enhancer.isAlreadySubclassed()) { - debugBytecodes(bc); + final byte[] byteArray = AsmHelper.toByteArray(cnt); + debugBytecodes(cnt, byteArray); // this is the new subclass - ClassLoader loader = GeneratedClasses.getMostDerivedLoader( - cls, PersistenceCapable.class); - subs.add(GeneratedClasses.loadBCClass(bc, loader)); + ClassLoader loader = GeneratedClasses.getMostDerivedLoader(cls, PersistenceCapable.class); + String className = cnt.getClassNode().name.replace("/", "."); + final Class subclass = GeneratedClasses.loadAsmClass(className, byteArray, cls, loader); + try { + // Ugly workaround to trigger clinit static initializer block :( + subclass.newInstance(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + subs.add(subclass); } } } - public static void debugBytecodes(BCClass bc) throws IOException { + public static void debugBytecodes(ClassNodeTracker cnt, byte[] classBytes) throws IOException { // Write the bytecodes to disk for debugging purposes. - if ("true".equals(System.getProperty( - ManagedClassSubclasser.class.getName() + ".dumpBytecodes"))) + if ("true".equals(System.getProperty(ManagedClassSubclasser.class.getName() + ".dumpBytecodes"))) { File tmp = new File(System.getProperty("java.io.tmpdir")); File dir = new File(tmp, "openjpa"); dir = new File(dir, "pcsubclasses"); dir.mkdirs(); - dir = Files.getPackageFile(dir, bc.getPackageName(), true); - File f = new File(dir, bc.getClassName() + ".class"); + File f = new File(dir, cnt.getClassNode().name + ".class"); + // START - ALLOW PRINT STATEMENTS System.err.println("Writing to " + f); // STOP - ALLOW PRINT STATEMENTS - AsmAdaptor.write(bc, f); + + Files.write(f.toPath(), classBytes, StandardOpenOption.WRITE); } } - private static void setIntercepting(OpenJPAConfiguration conf, - ClassLoader envLoader, Class<?> cls) { - ClassMetaData meta = conf.getMetaDataRepositoryInstance() - .getMetaData(cls, envLoader, true); + private static void setIntercepting(OpenJPAConfiguration conf, ClassLoader envLoader, Class<?> cls) { + ClassMetaData meta = conf.getMetaDataRepositoryInstance().getMetaData(cls, envLoader, true); meta.setIntercepting(true); } 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 0dc51236d..d78299622 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 @@ -212,7 +212,6 @@ public class PCEnhancer { private final ClassMetaData _meta; private final Log _log; - private Collection _oids = null; private boolean _defCons = true; private boolean _redefine = false; private boolean _subclass = false; @@ -389,8 +388,8 @@ public class PCEnhancer { * except when running the enhancer to redefine and subclass * existing persistent types. */ - public BCClass getManagedTypeBytecode() { - return _managedType; + public ClassNodeTracker getManagedTypeBytecode() { + return managedType; } /** @@ -641,46 +640,41 @@ public class PCEnhancer { /** * Write the generated bytecode. */ - public void record() - throws IOException { - if (_managedType != _pc && getRedefine()) { - record(AsmHelper.toClassWriter(_managedType)); + public void record() throws IOException { + if (managedType != pc && getRedefine()) { + record(managedType); } - record(AsmHelper.toClassWriter(_pc)); - - if (_oids != null) - for (Object oid : _oids) { - record(AsmHelper.toClassWriter((BCClass) oid)); - } + record(pc); } /** * Write the given class. */ - private void record(ClassWriterTracker cwt) + private void record(ClassNodeTracker cnt) throws IOException { - if (_writer != null) - _writer.write(AsmHelper.toBCClass(cwt)); + if (_writer != null) { + _writer.write(cnt); + } else if (_dir == null) { - String name = cwt.getName().replace(".", "/"); - ClassLoader cl = cwt.getClassLoader(); + String name = cnt.getClassNode().name.replace(".", "/"); + ClassLoader cl = cnt.getClassLoader(); if (cl == null) { cl = Thread.currentThread().getContextClassLoader(); } final URL resource = cl.getResource(name + ".class"); try (OutputStream out = new FileOutputStream(URLDecoder.decode(resource.getFile()))) { - out.write(cwt.getCw().toByteArray()); + out.write(AsmHelper.toByteArray(cnt)); out.flush(); } } else { - String name = cwt.getName().replace(".", "/") + ".class"; + String name = cnt.getClassNode().name.replace(".", "/") + ".class"; File targetFile = new File(_dir, name); if (!targetFile.getParentFile().exists()) { targetFile.getParentFile().mkdirs(); } - java.nio.file.Files.write(targetFile.toPath(), cwt.getCw().toByteArray()); + java.nio.file.Files.write(targetFile.toPath(), AsmHelper.toByteArray(cnt)); } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java index 654d06368..4ba872cae 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassLoaderProxyService.java @@ -106,6 +106,7 @@ public class ClassLoaderProxyService definePackageFor(pck, protectionDomain); } existing = super.defineClass(proxyClassName, proxyBytes, 0, proxyBytes.length); + resolveClass(existing); classes.put(key, existing); } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java index b6ddf2565..5073e5b6a 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneratedClasses.java @@ -56,7 +56,7 @@ public class GeneratedClasses { /** * Load the class represented by the given bytecode. - * @deprecated move to ASM + * @deprecated move to ASM {@link #loadAsmClass(String, byte[], Class, ClassLoader)} */ public static Class loadBCClass(BCClass bc, ClassLoader loader) { BCClassLoader bcloader = AccessController 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 6bb378e3f..d86bb6b85 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 @@ -110,6 +110,15 @@ public final class AsmHelper { return cwt; } + /** + * Create a byte[] of that class represented by the ClassNodeTracker + */ + public static byte[] toByteArray(ClassNodeTracker cnt) { + ClassWriter cw = new BCClassWriter(ClassWriter.COMPUTE_FRAMES, cnt.getClassLoader()); + cnt.getClassNode().accept(cw); + return cw.toByteArray(); + } + /** * temporary helper class to convert ClassWriterTracker to BCClass * @deprecated must get removed when done with migrating from Serp to ASM diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java index c69a72eb4..06af49c79 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/BytecodeWriter.java @@ -29,5 +29,5 @@ import serp.bytecode.BCClass; */ public interface BytecodeWriter { - void write(BCClass type) throws IOException; + void write(ClassNodeTracker type) throws IOException; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java index f139069e2..7189e7946 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/TestEnhancementWithMultiplePUs.java @@ -32,6 +32,8 @@ import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Options; import org.apache.openjpa.meta.MetaDataRepository; import org.apache.openjpa.persistence.test.AbstractCachedEMFTestCase; +import org.apache.openjpa.util.asm.ClassNodeTracker; +import org.apache.xbean.asm9.Type; import serp.bytecode.BCClass; import serp.bytecode.Project; @@ -84,9 +86,8 @@ public class TestEnhancementWithMultiplePUs Project project = new Project(); // make sure that the class is not already enhanced for some reason - String className = - "org.apache.openjpa.enhance.UnenhancedBootstrapInstance"; - BCClass bc = assertNotPC(loader, project, className); + String className = "org/apache/openjpa/enhance/UnenhancedBootstrapInstance"; + assertNotPC(loader, project, className); // build up a writer that just stores to a list so that we don't // mutate the disk. @@ -94,10 +95,9 @@ public class TestEnhancementWithMultiplePUs BytecodeWriter writer = new BytecodeWriter() { @Override - public void write(BCClass type) throws IOException { - assertTrue(Arrays.asList(type.getInterfaceNames()).contains( - PersistenceCapable.class.getName())); - written.add(type.getName()); + public void write(ClassNodeTracker cnt) throws IOException { + assertTrue(cnt.getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class))); + written.add(cnt.getClassNode().name); } }; @@ -135,9 +135,9 @@ public class TestEnhancementWithMultiplePUs BytecodeWriter writer = new BytecodeWriter() { @Override - public void write(BCClass type) throws IOException { - assertTrue(Arrays.asList(type.getInterfaceNames()).contains(PersistenceCapable.class.getName())); - written.add(type.getName()); + public void write(ClassNodeTracker cnt) throws IOException { + assertTrue(cnt.getClassNode().interfaces.contains(Type.getInternalName(PersistenceCapable.class))); + written.add(cnt.getClassNode().name); } }; @@ -156,8 +156,8 @@ public class TestEnhancementWithMultiplePUs // ensure that we do process the classes listed in the PUs assertTrue(written.contains( - "org.apache.openjpa.enhance.UnenhancedBootstrapInstance")); + "org/apache/openjpa/enhance/UnenhancedBootstrapInstance")); assertTrue(written.contains( - "org.apache.openjpa.enhance.UnenhancedBootstrapInstance2")); + "org/apache/openjpa/enhance/UnenhancedBootstrapInstance2")); } }