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 032c6fc7fedb9ae6230a6730cdf7ffb4246119e7 Author: Mark Struberg <strub...@apache.org> AuthorDate: Thu Jun 1 14:21:05 2023 +0200 OPENJPA-2911 Bridge from BCClass <-> ASM ClassNode This will allow us to smoothly migrate from Serp to ASM --- .../org/apache/openjpa/jdbc/sql/SQLBuffer.java | 10 +++--- .../org/apache/openjpa/enhance/PCEnhancer.java | 39 ++++++++++++-------- .../openjpa/meta/InterfaceImplGenerator.java | 2 +- .../org/apache/openjpa/util/asm/AsmHelper.java | 39 ++++++++++++++++++-- .../apache/openjpa/util/asm/ClassNodeTracker.java | 42 ++++++++++++++++++++++ .../enhance/TestEnhancementWithMultiplePUs.java | 3 +- 6 files changed, 112 insertions(+), 23 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java index ac052da20..661de54d6 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLBuffer.java @@ -142,9 +142,9 @@ public final class SQLBuffer if (!paramOnly) { if (sqlIndex == _sql.length()) - _sql.append(buf._sql.toString()); + _sql.append(buf._sql); else - _sql.insert(sqlIndex, buf._sql.toString()); + _sql.insert(sqlIndex, buf._sql); } if (buf._params != null) { @@ -326,7 +326,7 @@ public final class SQLBuffer if (o == null) _sql.append("NULL"); else if (o instanceof Raw) - _sql.append(o.toString()); + _sql.append(o); else { Class<?> type = Filters.wrap(o.getClass()); if (useParamToken || !validParamLiteralType(type)) { @@ -366,7 +366,7 @@ public final class SQLBuffer } else if ( type == Character.class ) { if (_dict.storeCharsAsNumbers) { - _sql.append(Integer.toString((Character) o)); + _sql.append(o); } else { _sql.append("'" + o.toString().replace("'", "''") + "'"); } @@ -375,7 +375,7 @@ public final class SQLBuffer // We store B(b)ooleans as ints. Convert _sql.append(_dict.getBooleanRepresentation().getRepresentation(b)); } else { - _sql.append(o.toString()); + _sql.append(o); } } } 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 9a0cb32b0..aef244847 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 @@ -96,12 +96,15 @@ import org.apache.openjpa.util.ShortId; 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.ClassWriterTracker; import org.apache.xbean.asm9.Opcodes; import org.apache.xbean.asm9.Type; 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.InsnNode; +import org.apache.xbean.asm9.tree.LdcInsnNode; import org.apache.xbean.asm9.tree.MethodNode; import org.apache.xbean.asm9.tree.VarInsnNode; @@ -289,8 +292,7 @@ public class PCEnhancer { * @param loader the environment classloader to use for loading * classes and resources. */ - public PCEnhancer(OpenJPAConfiguration conf, BCClass type, - MetaDataRepository repos, ClassLoader loader) { + public PCEnhancer(OpenJPAConfiguration conf, BCClass type, MetaDataRepository repos, ClassLoader loader) { _managedType = type; _pc = type; @@ -326,8 +328,7 @@ public class PCEnhancer { * * @since 1.1.0 */ - public PCEnhancer(MetaDataRepository repos, BCClass type, - ClassMetaData meta) { + public PCEnhancer(MetaDataRepository repos, BCClass type, ClassMetaData meta) { _managedType = type; _pc = type; @@ -373,14 +374,6 @@ public class PCEnhancer { return className; } - /** - * Constructor. Supply configuration, type, and metadata. - */ - public PCEnhancer(OpenJPAConfiguration conf, BCClass type, - ClassMetaData meta) { - this(conf, type, meta.getRepository()); - } - /** * Return the bytecode representation of the persistence-capable class * being manipulated. @@ -3076,10 +3069,17 @@ public class PCEnhancer { */ private void enhanceClass() { // make the class implement PersistenceCapable - _pc.declareInterface(PCTYPE); + //_pc.declareInterface(PCTYPE); + + final ClassNodeTracker classNodeTracker = AsmHelper.toClassNode(_pc); + + // make the class implement PersistenceCapable + classNodeTracker.getClassNode().interfaces.add(Type.getInternalName(PCTYPE)); // add a version stamp - addGetEnhancementContractVersionMethod(); + addGetEnhancementContractVersionMethod(classNodeTracker); + + AsmHelper.readIntoBCClass(classNodeTracker, _pc); // find the default constructor BCMethod method = _pc.getDeclaredMethod("<init>", (String[]) null); @@ -4893,6 +4893,17 @@ public class PCEnhancer { return setter; } + private void addGetEnhancementContractVersionMethod(ClassNodeTracker cnt) { + MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC, + PRE + "GetEnhancementContractVersion", + Type.getMethodDescriptor(Type.INT_TYPE), + null, null); + methodNode.instructions.add(new LdcInsnNode(ENHANCER_VERSION)); + methodNode.instructions.add(new InsnNode(Opcodes.IRETURN)); + cnt.getClassNode().methods.add(methodNode); + } + + @Deprecated //X TODO REMOVE private void addGetEnhancementContractVersionMethod() { // public int getEnhancementContractVersion() BCMethod method = _pc.declareMethod(PRE + diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java index 1ae8f1adc..474e720a3 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/InterfaceImplGenerator.java @@ -124,7 +124,7 @@ class InterfaceImplGenerator { iface)).setFatal(true); try { // load the Class<?> for real. - impl = Class.forName(bc.getName(), true, enhLoader); + impl = Class.forName(enhancer.getPCBytecode().getName(), true, enhLoader); } catch (Throwable t) { throw new InternalException(_loc.get("interface-load2", iface, enhLoader), t).setFatal(true); 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 1f44aba54..5a819dc14 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 @@ -19,6 +19,7 @@ package org.apache.openjpa.util.asm; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.xbean.asm9.ClassReader; @@ -78,7 +79,7 @@ public final class AsmHelper { } /** - * temporary helper class to convert BCClass to ASM + * temporary helper class to convert BCClass to ASM ClassWriter * @deprecated must get removed when done with migrating from Serp to ASM */ public static ClassWriterTracker toClassWriter(BCClass bcClass) { @@ -102,10 +103,44 @@ public final class AsmHelper { return bcClass; } + /** + * temporary helper class to convert BCClass to ASM ClassNode + * @deprecated must get removed when done with migrating from Serp to ASM + */ + public static ClassNodeTracker toClassNode(BCClass bcClass) { + ClassReader cr = new ClassReader(bcClass.toByteArray()); + ClassNode classNode = new ClassNode(Opcodes.ASM9); + cr.accept(classNode, 0); + + return new ClassNodeTracker(classNode, bcClass.getClassLoader()); + } + + /** + * Take the changes from ClassNodeTracker and read it into the given BCClass instance. + * Effectively replace all the content of BCClass with the content from our ClassNode + */ + public static void readIntoBCClass(ClassNodeTracker cnt, BCClass bcClass) { + + // sadly package scoped + try { + Method readMethod = BCClass.class.getDeclaredMethod("read", InputStream.class, ClassLoader.class); + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + cnt.getClassNode().accept(cw); + final byte[] classBytes = cw.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(classBytes); + + readMethod.setAccessible(true); + readMethod.invoke(bcClass, bais, bcClass.getClassLoader()); + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } /** * Calclates the proper Return instruction opcode for the given class - * + * * @param type the type to get returned * @return the proper Opcode RETURN, ARETURN, IRETURN, etc */ diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java new file mode 100644 index 000000000..a9e911421 --- /dev/null +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/ClassNodeTracker.java @@ -0,0 +1,42 @@ +/* + * 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.tree.ClassNode; + +/** + * Helper class to transit from BCClass to ASM + * + * @author <a href="mailto:strub...@apache.org">Mark Struberg</a> + */ +public class ClassNodeTracker { + private final ClassNode classNode; + private final ClassLoader cl; + + public ClassNodeTracker(ClassNode classNode, ClassLoader cl) { + this.classNode = classNode; + this.cl = cl; + } + + public ClassNode getClassNode() { + return classNode; + } + + public ClassLoader getClassLoader() { + return cl; + } +} 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 a6c63ac33..875358865 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 @@ -56,7 +56,8 @@ public class TestEnhancementWithMultiplePUs PCEnhancer enhancer = new PCEnhancer(conf, bc, repos, loader); assertEquals(PCEnhancer.ENHANCE_PC, enhancer.run()); - assertTrue(Arrays.asList(bc.getInterfaceNames()).contains( + + assertTrue(Arrays.asList(enhancer.getPCBytecode().getInterfaceNames()).contains( PersistenceCapable.class.getName())); }