You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2023/05/06 07:18:36 UTC
[openjpa] 11/17: OPENJPA-2909 rough implementation with ASM is ready
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 8f9547b643421dcd88975e2ac4a283b11cace51b
Author: Mark Struberg <st...@apache.org>
AuthorDate: Wed May 3 23:16:59 2023 +0200
OPENJPA-2909 rough implementation with ASM is ready
Still needs a few tweaks to iron out failures.
---
openjpa-kernel/pom.xml | 2 -
.../org/apache/openjpa/util/ProxyManagerImpl.java | 956 +++++----------------
.../openjpa/util/proxy/ProxyConcurrentMaps.java | 57 --
.../org/apache/openjpa/util/proxy/ProxyMaps.java | 29 +
.../org/apache/openjpa/util/TestProxyManager.java | 16 +
5 files changed, 252 insertions(+), 808 deletions(-)
diff --git a/openjpa-kernel/pom.xml b/openjpa-kernel/pom.xml
index c9e5538b0..71b7fe094 100644
--- a/openjpa-kernel/pom.xml
+++ b/openjpa-kernel/pom.xml
@@ -109,7 +109,6 @@
<goal>run</goal>
</goals>
</execution>
-<!--
<execution>
<id>generate-standard-sco-proxies</id>
<phase>process-classes</phase>
@@ -124,7 +123,6 @@
<goal>run</goal>
</goals>
</execution>
--->
</executions>
</plugin>
<plugin>
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 8b4455180..32d541d68 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
@@ -26,7 +26,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -50,7 +49,6 @@ import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.openjpa.enhance.AsmAdaptor;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.util.ClassUtil;
import org.apache.openjpa.lib.util.Files;
@@ -70,7 +68,6 @@ import org.apache.openjpa.util.proxy.ProxyBean;
import org.apache.openjpa.util.proxy.ProxyCalendar;
import org.apache.openjpa.util.proxy.ProxyCollection;
import org.apache.openjpa.util.proxy.ProxyCollections;
-import org.apache.openjpa.util.proxy.ProxyConcurrentMaps;
import org.apache.openjpa.util.proxy.ProxyDate;
import org.apache.openjpa.util.proxy.ProxyMap;
import org.apache.openjpa.util.proxy.ProxyMaps;
@@ -80,12 +77,6 @@ import org.apache.xbean.asm9.MethodVisitor;
import org.apache.xbean.asm9.Opcodes;
import org.apache.xbean.asm9.Type;
-import serp.bytecode.BCClass;
-import serp.bytecode.BCField;
-import serp.bytecode.BCMethod;
-import serp.bytecode.Code;
-import serp.bytecode.JumpInstruction;
-import serp.bytecode.Project;
/**
* Default implementation of the {@link ProxyManager} interface.
@@ -416,8 +407,7 @@ public class ProxyManagerImpl
ProxyMap.class);
Class pcls = loadBuildTimeProxy(type, l);
if (pcls == null)
- pcls = GeneratedClasses.loadBCClass(
- generateProxyMapBytecode(type, true), l);
+ pcls = generateAndLoadProxyMap(type, true, l);
proxy = (ProxyMap) instantiateProxy(pcls, null, null);
_proxies.put(type, proxy);
}
@@ -493,15 +483,7 @@ public class ProxyManagerImpl
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type, ProxyBean.class);
Class<?> pcls = loadBuildTimeProxy(type, l);
if (pcls == null) {
- // TODO Move this to J2DOPrivHelper?
- BCClass bc = AccessController.doPrivileged(new PrivilegedAction<BCClass>() {
- @Override
- public BCClass run() {
- return generateProxyBeanBytecode(type, true);
- }
- });
- if (bc != null)
- pcls = GeneratedClasses.loadBCClass(bc, l);
+ pcls = generateAndLoadProxyBean(type, true, l);
}
if (pcls != null)
proxy = (ProxyBean) instantiateProxy(pcls, findCopyConstructor(type), new Object[] { orig });
@@ -595,32 +577,10 @@ public class ProxyManagerImpl
}
}
- /**
- * Generate the bytecode for a collection proxy for the given type.
- */
- protected BCClass generateProxyCollectionBytecode(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(ProxyCollection.class);
-
- delegateConstructors(bc, type);
- addProxyMethods(bc, false);
- addProxyCollectionMethods(bc, type);
- proxyRecognizedMethods(bc, type, ProxyCollections.class,
- ProxyCollection.class);
- proxySetters(bc, type);
- addWriteReplaceMethod(bc, runtime);
- return bc;
- }
-
/**
* Return the name of the proxy class to generate for the given type.
*/
- private static String getProxyClassName(Class type, boolean runtime) {
+ protected static String getProxyClassName(Class type, boolean runtime) {
String id = (runtime) ? "$" + nextProxyId() : "";
return ClassUtil.getPackageName(ProxyManagerImpl.class) + "."
+ type.getName().replace('.', '$') + id + PROXY_SUFFIX;
@@ -649,27 +609,6 @@ public class ProxyManagerImpl
}
- /**
- * Generate the bytecode for a map proxy for the given type.
- */
- protected BCClass generateProxyMapBytecode(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(ProxyMap.class);
-
- delegateConstructors(bc, type);
- addProxyMethods(bc, false);
- addProxyMapMethods(bc, type);
- Class<? extends ProxyMaps> mapProxyClassType = ProxyConcurrentMaps.class;
- proxyRecognizedMethods(bc, type, mapProxyClassType, ProxyMap.class);
- proxySetters(bc, type);
- addWriteReplaceMethod(bc, runtime);
- return bc;
- }
-
private Class generateAndLoadProxyDate(Class type, boolean runtime, ClassLoader l) {
final String proxyClassName = getProxyClassName(type, runtime);
@@ -692,6 +631,20 @@ public class ProxyManagerImpl
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
}
+ private Class generateAndLoadProxyMap(Class type, boolean runtime, ClassLoader l) {
+ final String proxyClassName = getProxyClassName(type, runtime);
+ final byte[] classBytes = generateProxyMapBytecode(type, runtime, proxyClassName);
+
+ return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
+ }
+
+ private Class generateAndLoadProxyBean(Class type, boolean runtime, ClassLoader l) {
+ final String proxyClassName = getProxyClassName(type, runtime);
+ final byte[] classBytes = generateProxyBeanBytecode(type, runtime, proxyClassName);
+
+ return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
+ }
+
/**
* Generate the bytecode for a date proxy for the given type.
*/
@@ -767,13 +720,182 @@ public class ProxyManagerImpl
addInstanceVariables(ct);
addProxyMethods(ct, false, proxyClassDef, type);
addProxyCollectionMethods(ct, proxyClassDef, type);
- proxyRecognizedMethods(ct, proxyClassDef, type,ProxyCollections.class, ProxyCollection.class);
+ proxyRecognizedMethods(ct, proxyClassDef, type, ProxyCollections.class, ProxyCollection.class);
proxySetters(ct, proxyClassDef, type);
addWriteReplaceMethod(ct, proxyClassDef, runtime);
return cw.toByteArray();
}
+ /**
+ * Generate the bytecode for a map proxy for the given type.
+ */
+ protected byte[] generateProxyMapBytecode(Class type, boolean runtime, String proxyClassName) {
+ assertNotFinal(type);
+ String proxyClassDef = proxyClassName.replace('.', '/');
+ String superClassFileNname = Type.getInternalName(type);
+ String[] interfaceNames = new String[]{Type.getInternalName(ProxyMap.class)};
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
+ null, superClassFileNname, interfaceNames);
+
+ ClassWriterTracker ct = new ClassWriterTracker(cw);
+ String classFileName = runtime ? type.getName() : proxyClassDef;
+ cw.visitSource(classFileName + ".java", null);
+
+ delegateConstructors(ct, type, superClassFileNname);
+ addInstanceVariables(ct);
+ addProxyMethods(ct, false, proxyClassDef, type);
+ addProxyCollectionMethods(ct, proxyClassDef, type);
+ proxyRecognizedMethods(ct, proxyClassDef, type, ProxyMaps.class, ProxyMap.class);
+ proxySetters(ct, proxyClassDef, type);
+ addWriteReplaceMethod(ct, proxyClassDef, runtime);
+
+ return cw.toByteArray();
+ }
+
+ /**
+ * Generate the bytecode for a bean proxy for the given type.
+ */
+ protected byte[] generateProxyBeanBytecode(Class type, boolean runtime, String proxyClassName) {
+ if (Modifier.isFinal(type.getModifiers())) {
+ return null;
+ }
+ if (ImplHelper.isManagedType(null, type)) {
+ return null;
+ }
+
+ // we can only generate a valid proxy if there is a copy constructor
+ // or a default constructor
+ Constructor cons = findCopyConstructor(type);
+ if (cons == null) {
+ Constructor[] cs = type.getConstructors();
+ for (int i = 0; cons == null && i < cs.length; i++) {
+ if (cs[i].getParameterTypes().length == 0) {
+ cons = cs[i];
+ }
+ }
+ if (cons == null)
+ return null;
+ }
+
+ String proxyClassDef = proxyClassName.replace('.', '/');
+ String superClassFileNname = Type.getInternalName(type);
+ String[] interfaceNames = new String[]{Type.getInternalName(ProxyBean.class)};
+
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
+ null, superClassFileNname, interfaceNames);
+
+ ClassWriterTracker ct = new ClassWriterTracker(cw);
+ String classFileName = runtime ? type.getName() : proxyClassDef;
+ cw.visitSource(classFileName + ".java", null);
+
+ delegateConstructors(ct, type, superClassFileNname);
+ addInstanceVariables(ct);
+ addProxyMethods(ct, true, proxyClassDef, type);
+ addProxyBeanMethods(ct, proxyClassDef, type, cons);
+ if (!proxySetters(ct, proxyClassDef, type)) {
+ return null;
+ }
+ addWriteReplaceMethod(ct, proxyClassDef, runtime);
+
+ return cw.toByteArray();
+ }
+
+ private void addProxyBeanMethods(ClassWriterTracker ct, String proxyClassDef, Class type, Constructor cons) {
+ // bean copy
+ {
+ MethodVisitor mv = ct.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);
+
+ Class[] params = cons.getParameterTypes();
+ if (params.length == 1) {
+ 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, AsmHelper.getParamTypes(params)), false);
+ int beanVarPos = params.length+2; // params+DUP
+
+ if (params.length == 0) {
+ mv.visitVarInsn(Opcodes.ASTORE, beanVarPos);
+ copyBeanProperties(mv, type, beanVarPos);
+ mv.visitVarInsn(Opcodes.ALOAD, beanVarPos);
+ }
+
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+
+ // new instance factory
+ {
+ MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
+ Type.getMethodDescriptor(Type.getType(ProxyBean.class), Type.getType(Object.class))
+ , null, null);
+ mv.visitCode();
+ mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
+ mv.visitInsn(Opcodes.DUP);
+
+
+ Class[] params = cons.getParameterTypes();
+ if (params.length == 1) {
+ 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, AsmHelper.getParamTypes(params)), false);
+ int beanVarPos = params.length+2; // params+DUP
+
+ if (params.length == 0) {
+ mv.visitVarInsn(Opcodes.ASTORE, beanVarPos);
+ copyBeanProperties(mv, type, beanVarPos);
+ mv.visitVarInsn(Opcodes.ALOAD, beanVarPos);
+ }
+
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+
+ }
+ }
+
+ private void copyBeanProperties(MethodVisitor mv, Class type, int copyVarPos) {
+ Method[] meths = type.getMethods();
+ Method getter;
+ int mods;
+ for (Method meth : meths) {
+ mods = meth.getModifiers();
+ if (!Modifier.isPublic(mods) || Modifier.isStatic(mods)) {
+ continue;
+ }
+
+ if (!startsWith(meth.getName(), "set")
+ || meth.getParameterTypes().length != 1) {
+ continue;
+ }
+
+ getter = findGetter(type, meth);
+ if (getter == null) {
+ continue;
+ }
+
+ // copy.setXXX(orig.getXXX());
+ mv.visitVarInsn(Opcodes.ALOAD, copyVarPos);
+ mv.visitVarInsn(Opcodes.ALOAD, copyVarPos-1);
+ mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(type));
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), getter.getName(),
+ Type.getMethodDescriptor(getter), false);
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), meth.getName(),
+ Type.getMethodDescriptor(meth), false);
+ }
+ }
private void addProxyCollectionMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
// change tracker
@@ -828,7 +950,7 @@ public class ProxyManagerImpl
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Collection.class));
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "addAll",
- Type.getMethodDescriptor(Type.BOOLEAN_TYPE, TYPE_OBJECT), true);
+ Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Collection.class)), false);
mv.visitInsn(Opcodes.POP);
}
@@ -1490,10 +1612,6 @@ public class ProxyManagerImpl
mv.visitEnd();
}
- /* a few utility methods to make life with ASM easier */
-
-
-
private boolean hasConstructor(Class type, Class<?>... paramTypes) {
try {
return type.getDeclaredConstructor(paramTypes) != null;
@@ -1503,258 +1621,6 @@ public class ProxyManagerImpl
}
}
- /* ASM end */
-
-
- /**
- * Generate the bytecode for a bean proxy for the given type.
- */
- protected BCClass generateProxyBeanBytecode(Class type, boolean runtime) {
- if (Modifier.isFinal(type.getModifiers()))
- return null;
- if (ImplHelper.isManagedType(null, type))
- return null;
-
- // we can only generate a valid proxy if there is a copy constructor
- // or a default constructor
- Constructor cons = findCopyConstructor(type);
- if (cons == null) {
- Constructor[] cs = type.getConstructors();
- for (int i = 0; cons == null && i < cs.length; i++)
- if (cs[i].getParameterTypes().length == 0)
- cons = cs[i];
- if (cons == null)
- return null;
- }
-
- Project project = new Project();
- BCClass bc = AccessController.doPrivileged(J2DoPrivHelper
- .loadProjectClassAction(project, getProxyClassName(type, runtime)));
- bc.setSuperclass(type);
- bc.declareInterface(ProxyBean.class);
-
- delegateConstructors(bc, type);
- addProxyMethods(bc, true);
- addProxyBeanMethods(bc, type, cons);
- if (!proxySetters(bc, type))
- return null;
- addWriteReplaceMethod(bc, runtime);
- return bc;
- }
-
- /**
- * Create pass-through constructors to base type.
- */
- private void delegateConstructors(BCClass bc, Class type) {
- Constructor[] cons = type.getConstructors();
- Class[] params;
- BCMethod m;
- Code code;
- for (Constructor con : cons) {
- params = con.getParameterTypes();
- m = bc.declareMethod("<init>", void.class, params);
- m.makePublic();
-
- code = m.getCode(true);
- code.aload().setThis();
- for (int j = 0; j < params.length; j++)
- code.xload().setParam(j).setType(params[j]);
- code.invokespecial().setMethod(con);
- code.vreturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
- }
-
- /**
- * 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
- */
- private void addProxyMethods(BCClass bc, boolean changeTracker) {
- BCField sm = bc.declareField("sm", OpenJPAStateManager.class);
- sm.setTransient(true);
- BCField field = bc.declareField("field", int.class);
- field.setTransient(true);
-
- BCMethod m = bc.declareMethod("setOwner", void.class, new Class[] {
- OpenJPAStateManager.class, int.class });
- m.makePublic();
- Code code = m.getCode(true);
- code.aload().setThis();
- code.aload().setParam(0);
- code.putfield().setField(sm);
- code.aload().setThis();
- code.iload().setParam(1);
- code.putfield().setField(field);
- code.vreturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- m = bc.declareMethod("getOwner", OpenJPAStateManager.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(sm);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- m = bc.declareMethod("getOwnerField", int.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(field);
- code.ireturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- /*
- * 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).
- */
- m = bc.declareMethod("clone", Object.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.invokespecial().setMethod(bc.getSuperclassType(), "clone",
- Object.class, null);
- code.checkcast().setType(Proxy.class);
- int other = code.getNextLocalsIndex();
- code.astore().setLocal(other);
- code.aload().setLocal(other);
- code.constant().setNull();
- code.constant().setValue(0);
- code.invokeinterface().setMethod(Proxy.class, "setOwner", void.class,
- new Class[] { OpenJPAStateManager.class, int.class });
- code.aload().setLocal(other);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- if (changeTracker) {
- m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.constant().setNull();
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
- }
-
- /**
- * Implement the methods in the {@link ProxyCollection} interface.
- */
- private void addProxyCollectionMethods(BCClass bc, Class type) {
- // change tracker
- BCField changeTracker = bc.declareField("changeTracker",
- CollectionChangeTracker.class);
- changeTracker.setTransient(true);
- BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class,
- null);
- m.makePublic();
- Code code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(changeTracker);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // collection copy
- Constructor cons = findCopyConstructor(type);
- if (cons == null && SortedSet.class.isAssignableFrom(type))
- cons = findComparatorConstructor(type);
- Class[] params = (cons == null) ? new Class[0]
- : cons.getParameterTypes();
-
- m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
- m.makePublic();
- code = m.getCode(true);
-
- code.anew().setType(type);
- code.dup();
- if (params.length == 1) {
- code.aload().setParam(0);
- if (params[0] == Comparator.class) {
- code.checkcast().setType(SortedSet.class);
- code.invokeinterface().setMethod(SortedSet.class, "comparator",
- Comparator.class, null);
- } else
- code.checkcast().setType(params[0]);
- }
- code.invokespecial().setMethod(type, "<init>", void.class, params);
- if (params.length == 0 || params[0] == Comparator.class) {
- code.dup();
- code.aload().setParam(0);
- code.checkcast().setType(Collection.class);
- code.invokevirtual().setMethod(type, "addAll", boolean.class,
- new Class[] { Collection.class });
- code.pop();
- }
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // element type
- BCField elementType = bc.declareField("elementType", Class.class);
- elementType.setTransient(true);
- m = bc.declareMethod("getElementType", Class.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(elementType);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // new instance factory
- m = bc.declareMethod("newInstance", ProxyCollection.class,
- new Class[] { Class.class, Comparator.class, boolean.class, boolean.class });
- m.makePublic();
- code = m.getCode(true);
-
- code.anew().setType(bc);
- code.dup();
- cons = findComparatorConstructor(type);
- params = (cons == null) ? new Class[0] : cons.getParameterTypes();
- if (params.length == 1)
- code.aload().setParam(1);
- code.invokespecial().setMethod("<init>", void.class, params);
- int ret = code.getNextLocalsIndex();
- code.astore().setLocal(ret);
-
- // set element type
- code.aload().setLocal(ret);
- code.aload().setParam(0);
- code.putfield().setField(elementType);
-
- // create change tracker and set it
- code.iload().setParam(2);
- JumpInstruction ifins = code.ifeq();
- code.aload().setLocal(ret);
- code.anew().setType(CollectionChangeTrackerImpl.class);
- code.dup();
- code.aload().setLocal(ret);
- code.constant().setValue(allowsDuplicates(type));
- code.constant().setValue(isOrdered(type));
- code.aload().setParam(3);
- code.invokespecial().setMethod(CollectionChangeTrackerImpl.class,
- "<init>", void.class, new Class[] { Collection.class,
- boolean.class, boolean.class, boolean.class });
- code.putfield().setField(changeTracker);
-
- ifins.setTarget(code.aload().setLocal(ret));
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
/**
* Return whether the given collection type allows duplicates.
*/
@@ -1771,261 +1637,6 @@ public class ProxyManagerImpl
|| "java.util.LinkedHashSet".equals(type.getName());
}
- /**
- * Implement the methods in the {@link ProxyMap} interface.
- */
- private void addProxyMapMethods(BCClass bc, Class type) {
- // change tracker
- BCField changeTracker = bc.declareField("changeTracker",
- MapChangeTracker.class);
- changeTracker.setTransient(true);
- BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class,
- null);
- m.makePublic();
- Code code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(changeTracker);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // map copy
- Constructor cons = findCopyConstructor(type);
- if (cons == null && SortedMap.class.isAssignableFrom(type))
- cons = findComparatorConstructor(type);
- Class[] params = (cons == null) ? new Class[0]
- : cons.getParameterTypes();
-
- m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
- m.makePublic();
- code = m.getCode(true);
-
- code.anew().setType(type);
- code.dup();
- if (params.length == 1) {
- code.aload().setParam(0);
- if (params[0] == Comparator.class) {
- code.checkcast().setType(SortedMap.class);
- code.invokeinterface().setMethod(SortedMap.class, "comparator",
- Comparator.class, null);
- } else
- code.checkcast().setType(params[0]);
- }
- code.invokespecial().setMethod(type, "<init>", void.class, params);
- if (params.length == 0 || params[0] == Comparator.class) {
- code.dup();
- code.aload().setParam(0);
- code.checkcast().setType(Map.class);
- code.invokevirtual().setMethod(type, "putAll", void.class,
- new Class[] { Map.class });
- }
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // key type
- BCField keyType = bc.declareField("keyType", Class.class);
- keyType.setTransient(true);
- m = bc.declareMethod("getKeyType", Class.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(keyType);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // value type
- BCField valueType = bc.declareField("valueType", Class.class);
- valueType.setTransient(true);
- m = bc.declareMethod("getValueType", Class.class, null);
- m.makePublic();
- code = m.getCode(true);
- code.aload().setThis();
- code.getfield().setField(valueType);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // new instance factory
- m = bc.declareMethod("newInstance", ProxyMap.class,
- new Class[] { Class.class, Class.class, Comparator.class,
- boolean.class,boolean.class });
- m.makePublic();
- code = m.getCode(true);
-
- code.anew().setType(bc);
- code.dup();
- cons = findComparatorConstructor(type);
- params = (cons == null) ? new Class[0] : cons.getParameterTypes();
- if (params.length == 1)
- code.aload().setParam(2);
- code.invokespecial().setMethod("<init>", void.class, params);
- int ret = code.getNextLocalsIndex();
- code.astore().setLocal(ret);
-
- // set key and value types
- code.aload().setLocal(ret);
- code.aload().setParam(0);
- code.putfield().setField(keyType);
- code.aload().setLocal(ret);
- code.aload().setParam(1);
- code.putfield().setField(valueType);
-
- // create change tracker and set it
- code.iload().setParam(3);
- JumpInstruction ifins = code.ifeq();
- code.aload().setLocal(ret);
- code.anew().setType(MapChangeTrackerImpl.class);
- code.dup();
- code.aload().setLocal(ret);
- code.aload().setParam(4);
- code.invokespecial().setMethod(MapChangeTrackerImpl.class,
- "<init>", void.class, new Class[] { Map.class, boolean.class });
- code.putfield().setField(changeTracker);
-
- ifins.setTarget(code.aload().setLocal(ret));
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
- /**
- * Implement the methods in the {@link ProxyBean} interface.
- */
- private void addProxyBeanMethods(BCClass bc, Class type, Constructor cons) {
- // bean copy
- BCMethod m = bc.declareMethod("copy", Object.class,
- new Class[] { Object.class });
- m.makePublic();
- Code code = m.getCode(true);
-
- code.anew().setType(type);
- code.dup();
- Class[] params = cons.getParameterTypes();
- if (params.length == 1) {
- code.aload().setParam(0);
- code.checkcast().setType(params[0]);
- }
- code.invokespecial().setMethod(cons);
- if (params.length == 0)
- copyProperties(type, code);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
-
- // new instance factory
- m = bc.declareMethod("newInstance", ProxyBean.class,
- new Class[] { Object.class });
- m.makePublic();
- code = m.getCode(true);
- code.anew().setType(bc);
- code.dup();
- if (params.length == 1) {
- code.aload().setParam(0);
- code.checkcast().setType(params[0]);
- }
- code.invokespecial().setMethod("<init>", void.class, params);
- if (params.length == 0)
- copyProperties(type, code);
- code.areturn();
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
- /**
- * Copy bean properties. Called with the copy object on the stack. Must
- * return with the copy object on the stack.
- */
- private void copyProperties(Class type, Code code) {
- int copy = code.getNextLocalsIndex();
- code.astore().setLocal(copy);
-
- Method[] meths = type.getMethods();
- Method getter;
- int mods;
- for (Method meth : meths) {
- mods = meth.getModifiers();
- if (!Modifier.isPublic(mods) || Modifier.isStatic(mods))
- continue;
- if (!startsWith(meth.getName(), "set")
- || meth.getParameterTypes().length != 1)
- continue;
- getter = findGetter(type, meth);
- if (getter == null)
- continue;
-
- // copy.setXXX(orig.getXXX());
- code.aload().setLocal(copy);
- code.aload().setParam(0);
- code.checkcast().setType(type);
- code.invokevirtual().setMethod(getter);
- code.invokevirtual().setMethod(meth);
- }
- code.aload().setLocal(copy);
- }
-
- /**
- * Proxy recognized methods to invoke helpers in given helper class.
- */
- private void proxyRecognizedMethods(BCClass bc, Class type, Class helper,
- Class proxyType) {
- Method[] meths = type.getMethods();
- Class[] params;
- Class[] afterParams;
- Method match;
- Method after;
- for (Method meth : meths) {
- // Java 8 methods with a return type of KeySetView do not need to be proxied
- if (meth.getReturnType().getName().contains("KeySetView")) continue;
-
- params = toHelperParameters(meth.getParameterTypes(),
- proxyType);
-
- // first check for overriding method
- try {
- match = helper.getMethod(meth.getName(), params);
- proxyOverrideMethod(bc, meth, match, params);
- continue;
- }
- catch (NoSuchMethodException nsme) {
- }
- catch (Exception e) {
- throw new GeneralException(e);
- }
-
- // check for before and after methods, either of which may not
- // exist
- match = null;
- try {
- match = helper.getMethod("before"
- + StringUtil.capitalize(meth.getName()), params);
- }
- catch (NoSuchMethodException nsme) {
- }
- catch (Exception e) {
- throw new GeneralException(e);
- }
- after = null;
- afterParams = null;
- try {
- afterParams = toHelperAfterParameters(params,
- meth.getReturnType(), (match == null)
- ? void.class : match.getReturnType());
- after = helper.getMethod("after"
- + StringUtil.capitalize(meth.getName()), afterParams);
- }
- catch (NoSuchMethodException nsme) {
- }
- catch (Exception e) {
- throw new GeneralException(e);
- }
- if (match != null || after != null)
- proxyBeforeAfterMethod(bc, type, meth, match, params, after,
- afterParams);
- }
- }
-
/**
* Return the parameter types to the corresponding helper class method.
*/
@@ -2059,101 +1670,6 @@ public class ProxyManagerImpl
return params;
}
- /**
- * Proxy setter methods of the given type.
- *
- * @return true if we find any setters, false otherwise
- */
- private boolean proxySetters(BCClass bc, Class type) {
- Method[] meths = type.getMethods();
- int setters = 0;
- for (Method meth : meths) {
- if (isSetter(meth) && !Modifier.isFinal(meth.getModifiers())
- && bc.getDeclaredMethod(meth.getName(),
- meth.getParameterTypes()) == null) {
- setters++;
- proxySetter(bc, type, meth);
- }
- }
- return setters > 0;
- }
-
- /**
- * Proxy the given method with one that overrides it by calling into the
- * given helper.
- */
- private void proxyOverrideMethod(BCClass bc, Method meth,
- Method helper, Class[] params) {
- BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
- meth.getParameterTypes());
- m.makePublic();
- Code code = m.getCode(true);
-
- code.aload().setThis();
- for (int i = 1; i < params.length; i++)
- code.xload().setParam(i - 1).setType(params[i]);
- code.invokestatic().setMethod(helper);
- code.xreturn().setType(meth.getReturnType());
-
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
- /**
- * Proxy the given method with one that overrides it by calling into the
- * given helper.
- */
- private void proxyBeforeAfterMethod(BCClass bc, Class type, Method meth,
- Method before, Class[] params, Method after, Class[] afterParams) {
- BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
- meth.getParameterTypes());
- m.makePublic();
- Code code = m.getCode(true);
-
- // invoke before
- int beforeRet = -1;
- if (before != null) {
- code.aload().setThis();
- for (int i = 1; i < params.length; i++)
- code.xload().setParam(i - 1).setType(params[i]);
- code.invokestatic().setMethod(before);
- if (after != null && before.getReturnType() != void.class) {
- beforeRet = code.getNextLocalsIndex();
- code.xstore().setLocal(beforeRet).
- setType(before.getReturnType());
- }
- }
-
- // invoke super
- code.aload().setThis();
- for (int i = 1; i < params.length; i++)
- code.xload().setParam(i - 1).setType(params[i]);
- code.invokespecial().setMethod(type, meth.getName(),
- meth.getReturnType(), meth.getParameterTypes());
-
- // invoke after
- if (after != null) {
- int ret = -1;
- if (meth.getReturnType() != void.class) {
- ret = code.getNextLocalsIndex();
- code.xstore().setLocal(ret).setType(meth.getReturnType());
- }
- code.aload().setThis();
- for (int i = 1; i < params.length; i++)
- code.xload().setParam(i - 1).setType(params[i]);
- if (ret != -1)
- code.xload().setLocal(ret).setType(meth.getReturnType());
- if (beforeRet != -1)
- code.xload().setLocal(beforeRet).
- setType(before.getReturnType());
- code.invokestatic().setMethod(after);
- }
- code.xreturn().setType(meth.getReturnType());
-
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
/**
* Return whether the given method is a setter.
*/
@@ -2206,46 +1722,6 @@ public class ProxyManagerImpl
|| Character.isUpperCase(str.charAt(token.length())));
}
- /**
- * Proxy the given setter method to dirty the proxy owner.
- */
- private void proxySetter(BCClass bc, Class type, Method meth) {
- Class[] params = meth.getParameterTypes();
- Class ret = meth.getReturnType();
- BCMethod m = bc.declareMethod(meth.getName(), ret, params);
- m.makePublic();
- Code 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();
- for (int i = 0; i < params.length; i++)
- code.xload().setParam(i).setType(params[i]);
- code.invokespecial().setMethod(type, meth.getName(), ret, params);
- code.xreturn().setType(ret);
- code.calculateMaxStack();
- code.calculateMaxLocals();
- }
-
- /**
- * Add a writeReplace implementation that serializes to a non-proxy type
- * unless detached and this is a build-time generated class.
- */
- private void addWriteReplaceMethod(BCClass bc, boolean runtime) {
- BCMethod m = bc.declareMethod("writeReplace", Object.class, null);
- m.makeProtected();
- m.getExceptions(true).addException(ObjectStreamException.class);
- Code code = m.getCode(true);
- code.aload().setThis();
- code.constant().setValue(!runtime);
- code.invokestatic().setMethod(Proxies.class, "writeReplace",
- Object.class, new Class[] { Proxy.class, boolean.class });
- code.areturn();
- code.calculateMaxLocals();
- code.calculateMaxStack();
- }
-
/**
* Create a unique id to avoid proxy class name conflicts.
*/
@@ -2353,7 +1829,6 @@ public class ProxyManagerImpl
final ProxyManagerImpl mgr = new ProxyManagerImpl();
Class cls;
- BCClass bc;
for (Object type : types) {
cls = Class.forName((String) type);
try {
@@ -2366,48 +1841,31 @@ public class ProxyManagerImpl
// expected if the class hasn't been generated
}
- // ASM generated proxies
- if (Date.class.isAssignableFrom(cls) ||
- Calendar.class.isAssignableFrom(cls) ||
- Collection.class.isAssignableFrom(cls)) {
- final String proxyClassName = getProxyClassName(cls, false);
- byte[] bytes = null;
+ final String proxyClassName = getProxyClassName(cls, false);
- if (Date.class.isAssignableFrom(cls)) {
- bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName);
- }
- else if (Calendar.class.isAssignableFrom(cls)) {
- bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName);
- }
- else if (Collection.class.isAssignableFrom(cls)) {
- bytes = mgr.generateProxyCollectionBytecode(cls, false, proxyClassName);
- }
+ byte[] bytes = null;
- if (bytes != null) {
- final String fileName = cls.getName().replace('.', '$') + PROXY_SUFFIX + ".class";
- java.nio.file.Files.write(new File(dir, fileName).toPath(), bytes);
- }
- continue;
+ if (Date.class.isAssignableFrom(cls)) {
+ bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName);
+ }
+ else if (Calendar.class.isAssignableFrom(cls)) {
+ bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName);
+ }
+ else if (Collection.class.isAssignableFrom(cls)) {
+ bytes = mgr.generateProxyCollectionBytecode(cls, false, proxyClassName);
+ }
+ else if (Map.class.isAssignableFrom(cls)) {
+ bytes = mgr.generateProxyMapBytecode(cls, false, proxyClassName);
}
-
- else if (Map.class.isAssignableFrom(cls))
- bc = mgr.generateProxyMapBytecode(cls, false);
else {
- final Class fCls = cls;
- // TODO Move this to J2DOPrivHelper
- bc = AccessController
- .doPrivileged(new PrivilegedAction<BCClass>() {
- @Override
- public BCClass run() {
- return mgr.generateProxyBeanBytecode(fCls, false);
- }
- });
+ bytes = mgr.generateProxyBeanBytecode(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);
}
- // START - ALLOW PRINT STATEMENTS
- System.out.println(bc.getName());
- // STOP - ALLOW PRINT STATEMENTS
- AsmAdaptor.write(bc, new File(dir, bc.getClassName() + ".class"));
}
}
}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyConcurrentMaps.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyConcurrentMaps.java
deleted file mode 100644
index f2d32ec7e..000000000
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyConcurrentMaps.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.proxy;
-
-import java.util.Map;
-
-import org.apache.openjpa.util.MapChangeTracker;
-import org.apache.openjpa.util.Proxies;
-
-/**
- * Utility methods used by concurrent map proxies.
- *
- */
-public class ProxyConcurrentMaps extends ProxyMaps {
- /**
- * Call before invoking {@link Map#remove(key, value) } on super.
- */
- public static boolean beforeRemove(ProxyMap map, Object key, Object value) {
- Proxies.dirty(map, false);
- return map.containsKey(key);
- }
-
- /**
- * Call after invoking {@link Map#remove(key, value) } on super.
- *
- * @param ret the return value from the super's method
- * @param before the return value from {@link #beforeRemove}
- * @return the value to return from {@link Map#remove}
- */
- public static boolean afterRemove(ProxyMap map, Object key, Object value, boolean ret,
- boolean before) {
- if (before) {
- if (map.getChangeTracker() != null) {
- ((MapChangeTracker) map.getChangeTracker()).removed(key, ret);
- }
- Proxies.removed(map, key, true);
- Proxies.removed(map, ret, false);
- }
- return ret;
- }
-}
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyMaps.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyMaps.java
index 6d89c504b..b112a383b 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyMaps.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/proxy/ProxyMaps.java
@@ -206,6 +206,35 @@ public class ProxyMaps
return ret;
}
+ /**
+ * See OPENJPA-2442.
+ * Call before invoking {@link Map#remove(key, value) } on super.
+ */
+ public static boolean beforeRemove(ProxyMap map, Object key, Object value) {
+ Proxies.dirty(map, false);
+ return map.containsKey(key);
+ }
+
+ /**
+ * See OPENJPA-2442.
+ * Call after invoking {@link Map#remove(key, value) } on super.
+ *
+ * @param ret the return value from the super's method
+ * @param before the return value from {@link #beforeRemove}
+ * @return the value to return from {@link Map#remove}
+ */
+ public static boolean afterRemove(ProxyMap map, Object key, Object value, boolean ret,
+ boolean before) {
+ if (before) {
+ if (map.getChangeTracker() != null) {
+ ((MapChangeTracker) map.getChangeTracker()).removed(key, ret);
+ }
+ Proxies.removed(map, key, true);
+ Proxies.removed(map, ret, false);
+ }
+ return ret;
+ }
+
/**
* Marker interface for a proxy entry set.
*/
diff --git a/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java b/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java
index b2a4950c9..543486513 100644
--- a/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java
+++ b/openjpa-kernel/src/test/java/org/apache/openjpa/util/TestProxyManager.java
@@ -18,6 +18,7 @@
*/
package org.apache.openjpa.util;
+import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.Time;
@@ -47,6 +48,7 @@ import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
+import org.apache.openjpa.lib.util.Files;
import org.junit.Before;
import org.junit.Test;
@@ -769,6 +771,20 @@ public class TestProxyManager {
assertBeansEqual(orig, (CustomBean) _mgr.copyCustom(orig));
}
+
+ @Test
+ public void testBeanClassProxy() throws Exception {
+ Class cls = CustomComparatorSortedSet.class;
+ final String proxyClassName = ProxyManagerImpl.getProxyClassName(cls, false);
+ final byte[] bytes = _mgr.generateProxyCollectionBytecode(cls, true, proxyClassName);
+ File dir = Files.getClassFile(TestProxyManager.class).getParentFile();
+
+ final String fileName = cls.getName().replace('.', '$') + "$proxy" + ".class";
+ java.nio.file.Files.write(new File(dir, fileName).toPath(), bytes);
+
+ _mgr.loadBuildTimeProxy(cls, this.getClass().getClassLoader());
+ }
+
/**
* Populate the given bean with arbitrary data.
*/