OPENJPA-2909 generate ProxyDate
still wip
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 5d41ffa..6c43b9d 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 @@
*/
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,6 +719,270 @@
"field", Type.getDescriptor(int.class), null, null).visitEnd();
}
+
+ /**
+ * Create pass-through constructors to base type.
+ */
+ private void delegateConstructors(ClassWriter cw, Class type, String superClassFileNname) {
+ Constructor[] constructors = type.getConstructors();
+
+ for (Constructor constructor : constructors) {
+ Class[] params = constructor.getParameterTypes();
+ String[] exceptionTypes = getInternalNames(constructor.getExceptionTypes());
+ String descriptor = Type.getConstructorDescriptor(constructor);
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
+ mv.visitCode();
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ for (int i = 1; i <= params.length; i++)
+ {
+ mv.visitVarInsn(getVarInsn(params[i-1]), i);
+ }
+ mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassFileNname, "<init>", descriptor, false);
+
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+ }
+
+ /**
+ * 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 proxyClassDef
+ */
+ 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.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitVarInsn(Opcodes.ILOAD, 2);
+ mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "field", Type.getDescriptor(Integer.TYPE));
+
+ 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.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "sm", Type.getDescriptor(OpenJPAStateManager.class));
+
+ 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());
+
+ mv.visitInsn(Opcodes.IRETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ {
+ /*
+ * 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.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.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) {
+ MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getChangeTracker",
+ Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
+ , null, null);
+ mv.visitCode();
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ mv.visitInsn(Opcodes.ARETURN);
+ 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
*
@@ -766,136 +1030,7 @@
return Opcodes.ALOAD;
}
- /**
- * Create pass-through constructors to base type.
- */
- private void delegateConstructors(ClassWriter cw, Class type, String superClassFileNname) {
- Constructor[] constructors = type.getConstructors();
-
- for (Constructor constructor : constructors) {
- Class[] params = constructor.getParameterTypes();
- String[] exceptionTypes = getInternalNames(constructor.getExceptionTypes());
- String descriptor = Type.getConstructorDescriptor(constructor);
- MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
- mv.visitCode();
- mv.visitVarInsn(Opcodes.ALOAD, 0);
- for (int i = 1; i <= params.length; i++)
- {
- mv.visitVarInsn(getVarInsn(params[i-1]), i);
- }
- mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassFileNname, "<init>", descriptor, false);
-
- mv.visitInsn(Opcodes.RETURN);
- mv.visitMaxs(-1, -1);
- mv.visitEnd();
- }
- }
-
- 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
- */
- 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();
-
-
- 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.visitInsn(Opcodes.IRETURN);
- mv.visitMaxs(-1, -1);
- mv.visitEnd();
-
-
- mv = cw.visitMethod(Modifier.PUBLIC, "getOwnerField",
- Type.getMethodDescriptor(Type.INT_TYPE)
- , null, null);
- mv.visitCode();
-
- 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();
-
-
- /*
- * 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.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.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",
- Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
- , null, null);
- mv.visitCode();
- mv.visitInsn(Opcodes.ACONST_NULL);
- mv.visitInsn(Opcodes.ARETURN);
- mv.visitMaxs(-1, -1);
- mv.visitEnd();
- }
-
- }
-
+ /* ASM end */
/**
* Generate the bytecode for a calendar proxy for the given type.