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.
      */