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 65242c70b7296c581c98df5e5d4471846c879fec Author: Mark Struberg <strub...@apache.org> AuthorDate: Tue May 2 10:39:12 2023 +0200 OPENJPA-2909 generate ProxyDate still wip --- .../org/apache/openjpa/util/ProxyManagerImpl.java | 381 ++++++++++++++------- 1 file changed, 258 insertions(+), 123 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 5d41ffa2e..6c43b9dec 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 @@ -681,25 +681,25 @@ public class ProxyManagerImpl */ protected byte[] generateProxyDateBytecode(Class type, boolean runtime, String proxyClassName) { assertNotFinal(type); - proxyClassName = proxyClassName.replace('.', '/'); + String proxyClassDef = proxyClassName.replace('.', '/'); String superClassFileNname = type.getName().replace('.', '/'); String[] interfaceNames = new String[]{Type.getInternalName(ProxyDate.class)}; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassName, + cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef, null, superClassFileNname, interfaceNames); - String classFileName = runtime ? type.getName() : proxyClassName; + String classFileName = runtime ? type.getName() : proxyClassDef; cw.visitSource(classFileName + ".java", null); delegateConstructors(cw, type, superClassFileNname); addInstanceVariables(cw); - addProxyMethods(cw, true, proxyClassName, type); + addProxyMethods(cw, true, proxyClassDef, type); + addProxyDateMethods(cw, proxyClassDef, type); /* TODO - addProxyDateMethods(bc, type); proxySetters(bc, type); addWriteReplaceMethod(bc, runtime); */ @@ -719,52 +719,6 @@ public class ProxyManagerImpl "field", Type.getDescriptor(int.class), null, null).visitEnd(); } - /** - * Returns the appropriate bytecode instruction to load a value from a variable to the stack - * - * @param type Type to load - * @return Bytecode instruction to use - */ - private int getVarInsn(Class<?> type) - { - if (type.isPrimitive()) - { - if (Integer.TYPE.equals(type)) - { - return Opcodes.ILOAD; - } - else if (Boolean.TYPE.equals(type)) - { - return Opcodes.ILOAD; - } - else if (Character.TYPE.equals(type)) - { - return Opcodes.ILOAD; - } - else if (Byte.TYPE.equals(type)) - { - return Opcodes.ILOAD; - } - else if (Short.TYPE.equals(type)) - { - return Opcodes.ILOAD; - } - else if (Float.TYPE.equals(type)) - { - return Opcodes.FLOAD; - } - else if (Long.TYPE.equals(type)) - { - return Opcodes.LLOAD; - } - else if (Double.TYPE.equals(type)) - { - return Opcodes.DLOAD; - } - } - - return Opcodes.ALOAD; - } /** * Create pass-through constructors to base type. @@ -791,100 +745,95 @@ public class ProxyManagerImpl } } - private String[] getInternalNames(Class[] classes) { - String[] internalNames = new String[classes.length]; - - for (int i=0; i<classes.length; i++) { - internalNames[i] = Type.getInternalName(classes[i]); - } - return internalNames; - } - /** * Implement the methods in the {@link Proxy} interface, with the exception * of {@link Proxy#copy}. * * @param changeTracker whether to implement a null change tracker; if false * the change tracker method is left unimplemented - * @param proxyClassName + * @param proxyClassDef */ - private void addProxyMethods(ClassWriter cw, boolean changeTracker, String proxyClassName, Class<?> parentClass) { - - MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "setOwner", - Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE) - , null, null); - mv.visitCode(); - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitVarInsn(Opcodes.ALOAD, 1); - mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassName, "sm", Type.getDescriptor(OpenJPAStateManager.class)); - - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitVarInsn(Opcodes.ILOAD, 2); - mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassName, "field", Type.getDescriptor(Integer.TYPE)); - - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(-1, -1); - mv.visitEnd(); + private void addProxyMethods(ClassWriter cw, boolean changeTracker, String proxyClassDef, Class<?> parentClass) { + { + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "setOwner", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE) + , null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "sm", Type.getDescriptor(OpenJPAStateManager.class)); - mv = cw.visitMethod(Modifier.PUBLIC, "getOwner", - Type.getMethodDescriptor(Type.getType(OpenJPAStateManager.class), Type.VOID_TYPE) - , null, null); - mv.visitCode(); - - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassName, "sm", Type.getDescriptor(OpenJPAStateManager.class)); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 2); + mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "field", Type.getDescriptor(Integer.TYPE)); - mv.visitInsn(Opcodes.IRETURN); - mv.visitMaxs(-1, -1); - mv.visitEnd(); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + { + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwner", + Type.getMethodDescriptor(Type.getType(OpenJPAStateManager.class)) + , null, null); + mv.visitCode(); - mv = cw.visitMethod(Modifier.PUBLIC, "getOwnerField", - Type.getMethodDescriptor(Type.INT_TYPE) - , null, null); - mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "sm", Type.getDescriptor(OpenJPAStateManager.class)); - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassName, "field", Type.INT_TYPE.getDescriptor()); + mv.visitInsn(Opcodes.IRETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } - mv.visitInsn(Opcodes.IRETURN); - mv.visitMaxs(-1, -1); - mv.visitEnd(); + { + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwnerField", + Type.getMethodDescriptor(Type.INT_TYPE) + , null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "field", Type.INT_TYPE.getDescriptor()); - /* - * clone (return detached proxy object) - * Note: This method is only being provided to satisfy a quirk with - * the IBM JDK -- while comparing Calendar objects, the clone() method - * was invoked. So, we are now overriding the clone() method so as to - * provide a detached proxy object (null out the StateManager). - */ - mv = cw.visitMethod(Modifier.PUBLIC, "clone", - Type.getMethodDescriptor(Type.getType(Object.class)) - , null, null); - mv.visitCode(); + mv.visitInsn(Opcodes.IRETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), "clone", - Type.getMethodDescriptor(Type.getType(Object.class)), false); - mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Proxy.class)); - mv.visitVarInsn(Opcodes.ASTORE, 1); - mv.visitVarInsn(Opcodes.ALOAD, 1); + { + /* + * clone (return detached proxy object) + * Note: This method is only being provided to satisfy a quirk with + * the IBM JDK -- while comparing Calendar objects, the clone() method + * was invoked. So, we are now overriding the clone() method so as to + * provide a detached proxy object (null out the StateManager). + */ + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "clone", + Type.getMethodDescriptor(Type.getType(Object.class)) + , null, null); + mv.visitCode(); - mv.visitInsn(Opcodes.ACONST_NULL); - mv.visitInsn(Opcodes.ICONST_0); - mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(Proxy.class), "setOwner", - Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE), true); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), "clone", + Type.getMethodDescriptor(Type.getType(Object.class)), false); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Proxy.class)); + mv.visitVarInsn(Opcodes.ASTORE, 1); + mv.visitVarInsn(Opcodes.ALOAD, 1); - mv.visitVarInsn(Opcodes.ALOAD, 1); - mv.visitInsn(Opcodes.ARETURN); - mv.visitMaxs(-1, -1); - mv.visitEnd(); + mv.visitInsn(Opcodes.ACONST_NULL); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(Proxy.class), "setOwner", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE), true); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } if (changeTracker) { - mv = cw.visitMethod(Modifier.PUBLIC, "getChangeTracker", + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getChangeTracker", Type.getMethodDescriptor(Type.getType(ChangeTracker.class)) , null, null); mv.visitCode(); @@ -893,9 +842,195 @@ public class ProxyManagerImpl mv.visitMaxs(-1, -1); mv.visitEnd(); } + } + + /** + * Implement the methods in the {@link ProxyDate} interface. + */ + private void addProxyDateMethods(ClassWriter cw, String proxyClassDef, Class type) { + + final boolean hasDefaultCons = hasConstructor(type); + final boolean hasMillisCons = hasConstructor(type, long.class); + + if (!hasDefaultCons && !hasMillisCons) { + throw new UnsupportedException(_loc.get("no-date-cons", type)); + } + // add a default constructor that delegates to the millis constructor + if (!hasDefaultCons) { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE), null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(System.class), "currentTimeMillis", + Type.getMethodDescriptor(Type.LONG_TYPE), false); + + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false); + + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + { + // date copy + Constructor cons = findCopyConstructor(type); + Class[] params; + if (cons != null) { + params = cons.getParameterTypes(); + } + else if (hasMillisCons) { + params = new Class[]{long.class}; + } + else { + params = new Class[0]; + } + + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy", + Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class)) + , null, null); + mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type)); + mv.visitInsn(Opcodes.DUP); + + if (params.length == 1) { + if (params[0] == long.class) { + // call getTime on the given Date if the current type has a long constructor + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime", + Type.getMethodDescriptor(Type.LONG_TYPE), false); + } + else { + // otherwise just pass the parameter + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0])); + } + } + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false); + + if (params.length == 0) { + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime", + Type.getMethodDescriptor(Type.LONG_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTime", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false); + } + if ((params.length == 0 || params[0] == long.class) + && Timestamp.class.isAssignableFrom(type)) { + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Timestamp.class)); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Timestamp.class), "getNanos", + Type.getMethodDescriptor(Type.INT_TYPE), false); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setNanos", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false); + + } + + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + { + // new instance factory + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance", + Type.getMethodDescriptor(Type.getType(ProxyDate.class)) + , null, null); + mv.visitTypeInsn(Opcodes.NEW, proxyClassDef); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE), false); + + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + } + + + + /* a few utility methods to make life with ASM easier */ + + private String[] getInternalNames(Class[] classes) { + String[] internalNames = new String[classes.length]; + + for (int i=0; i<classes.length; i++) { + internalNames[i] = Type.getInternalName(classes[i]); + } + return internalNames; + } + + private Type[] getParamTypes(Class[] params) { + Type[] types = new Type[params.length]; + for (int i=0; i<types.length; i++) { + types[i] = Type.getType(params[i]); + } + + return types; + } + + private boolean hasConstructor(Class type, Class<?>... paramTypes) { + try { + return type.getDeclaredConstructor(paramTypes) != null; + } + catch (NoSuchMethodException e) { + return false; + } + } + + /** + * Returns the appropriate bytecode instruction to load a value from a variable to the stack + * + * @param type Type to load + * @return Bytecode instruction to use + */ + private int getVarInsn(Class<?> type) + { + if (type.isPrimitive()) + { + if (Integer.TYPE.equals(type)) + { + return Opcodes.ILOAD; + } + else if (Boolean.TYPE.equals(type)) + { + return Opcodes.ILOAD; + } + else if (Character.TYPE.equals(type)) + { + return Opcodes.ILOAD; + } + else if (Byte.TYPE.equals(type)) + { + return Opcodes.ILOAD; + } + else if (Short.TYPE.equals(type)) + { + return Opcodes.ILOAD; + } + else if (Float.TYPE.equals(type)) + { + return Opcodes.FLOAD; + } + else if (Long.TYPE.equals(type)) + { + return Opcodes.LLOAD; + } + else if (Double.TYPE.equals(type)) + { + return Opcodes.DLOAD; + } + } + + return Opcodes.ALOAD; } + /* ASM end */ /** * Generate the bytecode for a calendar proxy for the given type.