From 87458c708bc3971763f9dc79153b9ac8aee66c8c Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Tue, 2 May 2023 14:47:16 +0200 Subject: [PATCH] OPENJPA-2909 generate CalendarProxy via ASM --- .../apache/openjpa/util/ProxyManagerImpl.java | 301 ++++++++++-------- 1 file changed, 175 insertions(+), 126 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java index 47f75f9bb..e1a6ce40b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java @@ -98,6 +98,7 @@ public class ProxyManagerImpl private static final Localizer _loc = Localizer.forPackage (ProxyManagerImpl.class); + public static final Type TYPE_OBJECT = Type.getType(Object.class); private static long _proxyId = 0L; private static final Map _stdCollections = new HashMap(); @@ -469,8 +470,7 @@ public class ProxyManagerImpl ProxyCalendar.class); Class pcls = loadBuildTimeProxy(type, l); if (pcls == null) - pcls = GeneratedClasses.loadBCClass( - generateProxyCalendarBytecode(type, true), l); + pcls = generateAndLoadProxyCalendar(type, true, l); proxy = (ProxyCalendar) instantiateProxy(pcls, null, null); _proxies.put(type, proxy); } @@ -676,15 +676,20 @@ public class ProxyManagerImpl return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l); } + private Class generateAndLoadProxyCalendar(Class type, boolean runtime, ClassLoader l) { + final String proxyClassName = getProxyClassName(type, runtime); + final byte[] classBytes = generateProxyCalendarBytecode(type, runtime, proxyClassName); + + return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l); + } + /** * Generate the bytecode for a date proxy for the given type. */ protected byte[] generateProxyDateBytecode(Class type, boolean runtime, String proxyClassName) { assertNotFinal(type); String proxyClassDef = proxyClassName.replace('.', '/'); - - String superClassFileNname = type.getName().replace('.', '/'); - + String superClassFileNname = Type.getInternalName(type); String[] interfaceNames = new String[]{Type.getInternalName(ProxyDate.class)}; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); @@ -704,6 +709,34 @@ public class ProxyManagerImpl return cw.toByteArray(); } + /** + * Generate the bytecode for a calendar proxy for the given type. + */ + protected byte[] generateProxyCalendarBytecode(Class type, boolean runtime, String proxyClassName) { + assertNotFinal(type); + String proxyClassDef = proxyClassName.replace('.', '/'); + String superClassFileNname = Type.getInternalName(type); + String[] interfaceNames = new String[]{Type.getInternalName(ProxyCalendar.class)}; + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef, + null, superClassFileNname, interfaceNames); + + String classFileName = runtime ? type.getName() : proxyClassDef; + cw.visitSource(classFileName + ".java", null); + + delegateConstructors(cw, type, superClassFileNname); + addInstanceVariables(cw); + addProxyMethods(cw, true, proxyClassDef, type); + addProxyCalendarMethods(cw, proxyClassDef, type); + proxySetters(cw, proxyClassDef, type); + addWriteReplaceMethod(cw, proxyClassDef, runtime); + + return cw.toByteArray(); + + } + + /** * add the instance variables to the class to be generated */ @@ -808,13 +841,13 @@ public class ProxyManagerImpl * provide a detached proxy object (null out the StateManager). */ MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "clone", - Type.getMethodDescriptor(Type.getType(Object.class)) + Type.getMethodDescriptor(TYPE_OBJECT) , null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), "clone", - Type.getMethodDescriptor(Type.getType(Object.class)), false); + Type.getMethodDescriptor(TYPE_OBJECT), false); mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Proxy.class)); mv.visitVarInsn(Opcodes.ASTORE, 1); mv.visitVarInsn(Opcodes.ALOAD, 1); @@ -886,8 +919,9 @@ public class ProxyManagerImpl } MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy", - Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class)) + Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT) , null, null); + mv.visitCode(); mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type)); mv.visitInsn(Opcodes.DUP); @@ -939,6 +973,7 @@ public class ProxyManagerImpl MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance", Type.getMethodDescriptor(Type.getType(ProxyDate.class)) , null, null); + mv.visitCode(); mv.visitTypeInsn(Opcodes.NEW, proxyClassDef); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "", @@ -950,6 +985,130 @@ public class ProxyManagerImpl } } + private void addProxyCalendarMethods(ClassWriter cw, String proxyClassDef, Class type) { + // calendar copy + { + Constructor cons = findCopyConstructor(type); + Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes(); + + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy", + Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT) + , null, null); + mv.visitCode(); + + mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type)); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "", + Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false); + + // timeInMillis + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeInMillis", + Type.getMethodDescriptor(Type.LONG_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeInMillis", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false); + + // lenient + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "isLenient", + Type.getMethodDescriptor(Type.BOOLEAN_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setLenient", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.BOOLEAN_TYPE), false); + + // firstDayOfWeek + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getFirstDayOfWeek", + Type.getMethodDescriptor(Type.INT_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setFirstDayOfWeek", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false); + + // minimalDaysInFirstWeek + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getMinimalDaysInFirstWeek", + Type.getMethodDescriptor(Type.INT_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setMinimalDaysInFirstWeek", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false); + + // timeZone + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeZone", + Type.getMethodDescriptor(Type.getType(TimeZone.class)), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeZone", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(TimeZone.class)), false); + + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + // newInstance factory + { + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance", + Type.getMethodDescriptor(Type.getType(ProxyCalendar.class)) + , null, null); + mv.visitCode(); + mv.visitTypeInsn(Opcodes.NEW, proxyClassDef); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "", + Type.getMethodDescriptor(Type.VOID_TYPE), false); + + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + // proxy the protected computeFields method b/c it is called on + // mutate, and some setters are final and therefore not proxyable + { + MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "computeFields", + Type.getMethodDescriptor(Type.VOID_TYPE) + , null, null); + mv.visitCode(); + + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitInsn(Opcodes.ICONST_1); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "dirty", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false); + + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "computeFields", + Type.getMethodDescriptor(Type.VOID_TYPE), false); + + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + + /* + + // proxy the protected computeFields method b/c it is called on + // mutate, and some setters are final and therefore not proxyable + m = bc.declareMethod("computeFields", void.class, null); + m.makeProtected(); + code = m.getCode(true); + code.aload().setThis(); + code.constant().setValue(true); + code.invokestatic().setMethod(Proxies.class, "dirty", void.class, + new Class[] { Proxy.class, boolean.class }); + code.aload().setThis(); + code.invokespecial().setMethod(type, "computeFields", void.class, null); + code.vreturn(); + code.calculateMaxStack(); + code.calculateMaxLocals(); + */ + } + /** * Proxy setter methods of the given type. * @@ -1006,13 +1165,13 @@ public class ProxyManagerImpl */ private void addWriteReplaceMethod(ClassWriter cw, String proxyClassDef, boolean runtime) { MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "writeReplace", - Type.getMethodDescriptor(Type.getType(Object.class)) + Type.getMethodDescriptor(TYPE_OBJECT) , null, new String[]{Type.getInternalName(ObjectStreamException.class)}); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitInsn(runtime ? Opcodes.ICONST_0 : Opcodes.ICONST_1); // !runtime mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "writeReplace", - Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false); + Type.getMethodDescriptor(TYPE_OBJECT, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(-1, -1); @@ -1120,25 +1279,6 @@ public class ProxyManagerImpl /* ASM end */ - /** - * Generate the bytecode for a calendar proxy for the given type. - */ - protected BCClass generateProxyCalendarBytecode(Class type, - boolean runtime) { - assertNotFinal(type); - Project project = new Project(); - BCClass bc = AccessController.doPrivileged(J2DoPrivHelper - .loadProjectClassAction(project, getProxyClassName(type, runtime))); - bc.setSuperclass(type); - bc.declareInterface(ProxyCalendar.class); - - delegateConstructors(bc, type); - addProxyMethods(bc, true); - addProxyCalendarMethods(bc, type); - proxySetters(bc, type); - addWriteReplaceMethod(bc, runtime); - return bc; - } /** * Generate the bytecode for a bean proxy for the given type. @@ -1523,100 +1663,7 @@ public class ProxyManagerImpl code.calculateMaxStack(); code.calculateMaxLocals(); } - - /** - * Implement the methods in the {@link ProxyCalendar} interface. - */ - private void addProxyCalendarMethods(BCClass bc, Class type) { - // calendar copy - Constructor cons = findCopyConstructor(type); - Class[] params = (cons == null) ? new Class[0] - : cons.getParameterTypes(); - - BCMethod m = bc.declareMethod("copy", Object.class, - new Class[] {Object.class}); - m.makePublic(); - Code code = m.getCode(true); - - code.anew().setType(type); - code.dup(); - if (params.length == 1) { - code.aload().setParam(0); - code.checkcast().setType(params[0]); - } - code.invokespecial().setMethod(type, "", void.class, params); - if (params.length == 0) { - code.dup(); - code.aload().setParam(0); - code.checkcast().setType(Calendar.class); - code.invokevirtual().setMethod(Calendar.class, "getTimeInMillis", - long.class, null); - code.invokevirtual().setMethod(type, "setTimeInMillis", void.class, - new Class[] { long.class }); - - code.dup(); - code.aload().setParam(0); - code.checkcast().setType(Calendar.class); - code.invokevirtual().setMethod(Calendar.class, "isLenient", - boolean.class, null); - code.invokevirtual().setMethod(type, "setLenient", void.class, - new Class[] { boolean.class }); - - code.dup(); - code.aload().setParam(0); - code.checkcast().setType(Calendar.class); - code.invokevirtual().setMethod(Calendar.class, "getFirstDayOfWeek", - int.class, null); - code.invokevirtual().setMethod(type, "setFirstDayOfWeek", - void.class, new Class[] { int.class }); - - code.dup(); - code.aload().setParam(0); - code.checkcast().setType(Calendar.class); - code.invokevirtual().setMethod(Calendar.class, - "getMinimalDaysInFirstWeek", int.class, null); - code.invokevirtual().setMethod(type, "setMinimalDaysInFirstWeek", - void.class, new Class[] { int.class }); - - code.dup(); - code.aload().setParam(0); - code.checkcast().setType(Calendar.class); - code.invokevirtual().setMethod(Calendar.class, "getTimeZone", - TimeZone.class, null); - code.invokevirtual().setMethod(type, "setTimeZone", void.class, - new Class[] { TimeZone.class }); - } - code.areturn(); - code.calculateMaxStack(); - code.calculateMaxLocals(); - - // new instance factory - m = bc.declareMethod("newInstance", ProxyCalendar.class, null); - m.makePublic(); - code = m.getCode(true); - code.anew().setType(bc); - code.dup(); - code.invokespecial().setMethod("", void.class, null); - code.areturn(); - code.calculateMaxStack(); - code.calculateMaxLocals(); - - // proxy the protected computeFields method b/c it is called on - // mutate, and some setters are final and therefore not proxyable - m = bc.declareMethod("computeFields", void.class, null); - m.makeProtected(); - code = m.getCode(true); - code.aload().setThis(); - code.constant().setValue(true); - code.invokestatic().setMethod(Proxies.class, "dirty", void.class, - new Class[] { Proxy.class, boolean.class }); - code.aload().setThis(); - code.invokespecial().setMethod(type, "computeFields", void.class, null); - code.vreturn(); - code.calculateMaxStack(); - code.calculateMaxLocals(); - } - + /** * Implement the methods in the {@link ProxyBean} interface. */ @@ -2094,13 +2141,17 @@ public class ProxyManagerImpl } // ASM generated proxies - if (Date.class.isAssignableFrom(cls)) { + if (Date.class.isAssignableFrom(cls) || + Calendar.class.isAssignableFrom(cls)) { final String proxyClassName = getProxyClassName(cls, false); byte[] bytes = null; if (Date.class.isAssignableFrom(cls)) { bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName); } + else if (Calendar.class.isAssignableFrom(cls)) { + bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName); + } if (bytes != null) { final String fileName = cls.getName().replace('.', '$') + PROXY_SUFFIX + ".class"; java.nio.file.Files.write(new File(dir, fileName).toPath(), bytes); @@ -2112,8 +2163,6 @@ public class ProxyManagerImpl bc = mgr.generateProxyCollectionBytecode(cls, false); else if (Map.class.isAssignableFrom(cls)) bc = mgr.generateProxyMapBytecode(cls, false); - else if (Calendar.class.isAssignableFrom(cls)) - bc = mgr.generateProxyCalendarBytecode(cls, false); else { final Class fCls = cls; // TODO Move this to J2DOPrivHelper