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:35 UTC

[openjpa] 10/17: OPENJPA-2909 implement ASM proxy for Collections

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 12f780eb763d0e7a39faf74d4fdb58af0f7094d7
Author: Mark Struberg <st...@apache.org>
AuthorDate: Wed May 3 12:58:26 2023 +0200

    OPENJPA-2909 implement ASM proxy for Collections
---
 openjpa-kernel/pom.xml                             |   2 +
 .../apache/openjpa/util/ClassWriterTracker.java    |  58 ++
 .../org/apache/openjpa/util/ProxyManagerImpl.java  | 588 ++++++++++++++-------
 .../org/apache/openjpa/util/asm/AsmHelper.java     | 182 +++++++
 4 files changed, 651 insertions(+), 179 deletions(-)

diff --git a/openjpa-kernel/pom.xml b/openjpa-kernel/pom.xml
index 71b7fe094..c9e5538b0 100644
--- a/openjpa-kernel/pom.xml
+++ b/openjpa-kernel/pom.xml
@@ -109,6 +109,7 @@
                             <goal>run</goal>
                         </goals>
                     </execution>
+<!--
                     <execution>
                         <id>generate-standard-sco-proxies</id>
                         <phase>process-classes</phase>
@@ -123,6 +124,7 @@
                             <goal>run</goal>
                         </goals>
                     </execution>
+-->
                 </executions>
             </plugin>
             <plugin>
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassWriterTracker.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassWriterTracker.java
new file mode 100644
index 000000000..0bcee1d47
--- /dev/null
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/ClassWriterTracker.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xbean.asm9.ClassWriter;
+import org.apache.xbean.asm9.MethodVisitor;
+
+/**
+ * Helper to keep track of generated methods when using ASM ClassWriter.
+ *
+ * @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
+ */
+public class ClassWriterTracker {
+
+    private final ClassWriter cw;
+    private List<String> createdMethods = new ArrayList<>();
+
+    public ClassWriterTracker(ClassWriter cw) {
+        this.cw = cw;
+    }
+
+    public ClassWriter getCw() {
+        return cw;
+    }
+
+    public MethodVisitor visitMethod(final int access,
+                                    final String name,
+                                    final String descriptor,
+                                    final String signature,
+                                    final String[] exceptionTypes) {
+        MethodVisitor mv = cw.visitMethod(access, name, descriptor, signature, exceptionTypes);
+
+        createdMethods.add(name + descriptor);
+
+        return mv;
+    }
+
+    public boolean hasMethod(final String name, final String descriptor) {
+        return createdMethods.contains(name + descriptor);
+    }
+}
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 e1a6ce40b..8b4455180 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
@@ -58,6 +58,7 @@ import org.apache.openjpa.lib.util.J2DoPrivHelper;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.lib.util.Options;
 import org.apache.openjpa.lib.util.StringUtil;
+import org.apache.openjpa.util.asm.AsmHelper;
 import org.apache.openjpa.util.proxy.DelayedArrayListProxy;
 import org.apache.openjpa.util.proxy.DelayedHashSetProxy;
 import org.apache.openjpa.util.proxy.DelayedLinkedHashSetProxy;
@@ -74,6 +75,7 @@ import org.apache.openjpa.util.proxy.ProxyDate;
 import org.apache.openjpa.util.proxy.ProxyMap;
 import org.apache.openjpa.util.proxy.ProxyMaps;
 import org.apache.xbean.asm9.ClassWriter;
+import org.apache.xbean.asm9.Label;
 import org.apache.xbean.asm9.MethodVisitor;
 import org.apache.xbean.asm9.Opcodes;
 import org.apache.xbean.asm9.Type;
@@ -403,25 +405,6 @@ public class ProxyManagerImpl
         return null;
     }
 
-    /**
-     * Return the cached factory proxy for the given collection type.
-     */
-    private ProxyCollection getFactoryProxyCollection(Class type) {
-        // we don't lock here; ok if two proxies get generated for same type
-        ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
-        if (proxy == null) {
-            ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
-                ProxyCollection.class);
-            Class pcls = loadBuildTimeProxy(type, l);
-            if (pcls == null)
-                pcls = GeneratedClasses.loadBCClass(
-                    generateProxyCollectionBytecode(type, true), l);
-            proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
-            _proxies.put(type, proxy);
-        }
-        return proxy;
-    }
-
     /**
      * Return the cached factory proxy for the given map type.
      */
@@ -477,6 +460,25 @@ public class ProxyManagerImpl
         return proxy;
     }
 
+    /**
+     * Return the cached factory proxy for the given collection type.
+     */
+    private ProxyCollection getFactoryProxyCollection(Class type) {
+        // we don't lock here; ok if two proxies get generated for same type
+        ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
+        if (proxy == null) {
+            ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
+                    ProxyCollection.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null)
+                pcls = generateAndLoadProxyCollection(type, true, l);
+            proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
+            _proxies.put(type, proxy);
+        }
+        return proxy;
+    }
+
+
     /**
      * Return the cached factory proxy for the given bean type.
      */
@@ -683,6 +685,13 @@ public class ProxyManagerImpl
         return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
     }
 
+    private Class generateAndLoadProxyCollection(Class type, boolean runtime, ClassLoader l) {
+        final String proxyClassName = getProxyClassName(type, runtime);
+        final byte[] classBytes = generateProxyCollectionBytecode(type, runtime, proxyClassName);
+
+        return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
+    }
+
     /**
      * Generate the bytecode for a date proxy for the given type.
      */
@@ -696,15 +705,16 @@ public class ProxyManagerImpl
         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(cw, type, superClassFileNname);
-        addInstanceVariables(cw);
-        addProxyMethods(cw, true, proxyClassDef, type);
-        addProxyDateMethods(cw, proxyClassDef, type);
-        proxySetters(cw, proxyClassDef, type);
-        addWriteReplaceMethod(cw, proxyClassDef, runtime);
+        delegateConstructors(ct, type, superClassFileNname);
+        addInstanceVariables(ct);
+        addProxyMethods(ct, true, proxyClassDef, type);
+        addProxyDateMethods(ct, proxyClassDef, type);
+        proxySetters(ct, proxyClassDef, type);
+        addWriteReplaceMethod(ct, proxyClassDef, runtime);
 
         return cw.toByteArray();
     }
@@ -722,31 +732,351 @@ public class ProxyManagerImpl
         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);
+        addProxyCalendarMethods(ct, proxyClassDef, type);
+        proxySetters(ct, proxyClassDef, type);
+        addWriteReplaceMethod(ct, proxyClassDef, runtime);
+
+        return cw.toByteArray();
+    }
+
+    /**
+     * Generate the bytecode for a collection proxy for the given type.
+     */
+    protected byte[] generateProxyCollectionBytecode(Class type, boolean runtime, String proxyClassName) {
+        assertNotFinal(type);
+        String proxyClassDef = proxyClassName.replace('.', '/');
+        String superClassFileNname = Type.getInternalName(type);
+        String[] interfaceNames = new String[]{Type.getInternalName(ProxyCollection.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(cw, type, superClassFileNname);
-        addInstanceVariables(cw);
-        addProxyMethods(cw, true, proxyClassDef, type);
-        addProxyCalendarMethods(cw, proxyClassDef, type);
-        proxySetters(cw, proxyClassDef, type);
-        addWriteReplaceMethod(cw, proxyClassDef, runtime);
+        delegateConstructors(ct, type, superClassFileNname);
+        addInstanceVariables(ct);
+        addProxyMethods(ct, false, proxyClassDef, type);
+        addProxyCollectionMethods(ct, proxyClassDef, type);
+        proxyRecognizedMethods(ct, proxyClassDef, type,ProxyCollections.class, ProxyCollection.class);
+        proxySetters(ct, proxyClassDef, type);
+        addWriteReplaceMethod(ct, proxyClassDef, runtime);
 
         return cw.toByteArray();
+    }
+
+
+    private void addProxyCollectionMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
+        // change tracker
+        {
+            ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
+                    "changeTracker", Type.getDescriptor(CollectionChangeTracker.class), null, null).visitEnd();
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
+                    Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
+                    , null, null);
+            mv.visitCode();
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
+
+            mv.visitInsn(Opcodes.ARETURN);
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+        }
+
+        // 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();
 
+            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);
+
+            if (params.length == 1) {
+                mv.visitVarInsn(Opcodes.ALOAD, 1);
+                if (params[0] == Comparator.class) {
+                    mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(SortedSet.class));
+                    mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(SortedSet.class), "comparator",
+                            Type.getMethodDescriptor(Type.getType(Comparator.class)), true);
+                }
+                else {
+                    // otherwise just pass the parameter
+                    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);
+
+            if (params.length == 0 || params[0] == Comparator.class) {
+                mv.visitInsn(Opcodes.DUP);
+                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);
+                mv.visitInsn(Opcodes.POP);
+            }
+
+            mv.visitInsn(Opcodes.ARETURN);
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+        }
+
+        // element type
+        {
+            ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
+                    "elementType", Type.getDescriptor(Class.class), null, null).visitEnd();
+
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getElementType",
+                    Type.getMethodDescriptor(Type.getType(Class.class))
+                    , null, null);
+            mv.visitCode();
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
+
+            mv.visitInsn(Opcodes.ARETURN);
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+
+        }
+
+        // new instance factory
+        {
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
+                    Type.getMethodDescriptor(Type.getType(ProxyCollection.class),
+                            Type.getType(Class.class), Type.getType(Comparator.class), Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE)
+                    , null, null);
+            mv.visitCode();
+            mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
+            mv.visitInsn(Opcodes.DUP);
+
+            Constructor cons = findComparatorConstructor(type);
+            Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
+            if (params.length == 1) {
+                mv.visitVarInsn(Opcodes.ALOAD, 2);
+            }
+
+            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "<init>",
+                Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
+
+            mv.visitVarInsn(Opcodes.ASTORE, 5);
+            mv.visitVarInsn(Opcodes.ALOAD, 5);
+            mv.visitVarInsn(Opcodes.ALOAD, 1);
+            mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
+
+            mv.visitVarInsn(Opcodes.ILOAD, 3);
+            Label lNotTrack = new Label();
+            mv.visitJumpInsn(Opcodes.IFEQ, lNotTrack);
+            mv.visitVarInsn(Opcodes.ALOAD, 5);
+            mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(CollectionChangeTrackerImpl.class));
+
+            mv.visitInsn(Opcodes.DUP);
+            mv.visitVarInsn(Opcodes.ALOAD, 5);
+
+            mv.visitInsn(allowsDuplicates(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+            mv.visitInsn(isOrdered(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
+            mv.visitVarInsn(Opcodes.ILOAD, 4);
+            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(CollectionChangeTrackerImpl.class), "<init>",
+                    Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Collection.class),
+                                             Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE),
+                    false);
+            mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
+
+            mv.visitLabel(lNotTrack);
+            mv.visitVarInsn(Opcodes.ALOAD, 5);
+
+            mv.visitInsn(Opcodes.ARETURN);
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+        }
+    }
+
+    private void proxyRecognizedMethods(ClassWriterTracker ct, String proxyClassDef, Class<?> type,
+                                        Class<?> helper, Class<?> proxyType) {
+        Method[] meths = type.getMethods();
+
+        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;
+            }
+
+            Class[] helperParams = toHelperParameters(meth.getParameterTypes(), proxyType);
+
+            // first check for overriding method
+            try {
+                Method match;
+                match = helper.getMethod(meth.getName(), helperParams);
+                proxyOverrideMethod(ct, meth, match, helperParams);
+                continue;
+            }
+            catch (NoSuchMethodException nsme) {
+                // all fine
+            }
+            catch (Exception e) {
+                throw new GeneralException(e);
+            }
+
+            // check for before and after methods, either of which may not
+            // exist
+            Method before = null;
+            try {
+                before = helper.getMethod("before" + StringUtil.capitalize(meth.getName()), helperParams);
+            }
+            catch (NoSuchMethodException nsme) {
+                // all fine
+            }
+            catch (Exception e) {
+                throw new GeneralException(e);
+            }
+            Method after = null;
+            Class[] afterParams = null;
+
+            try {
+                afterParams = toHelperAfterParameters(helperParams,
+                        meth.getReturnType(), (before == null)
+                                ? void.class : before.getReturnType());
+                after = helper.getMethod("after"
+                        + StringUtil.capitalize(meth.getName()), afterParams);
+            }
+            catch (NoSuchMethodException nsme) {
+            }
+            catch (Exception e) {
+                throw new GeneralException(e);
+            }
+            if (before != null || after != null)
+                proxyBeforeAfterMethod(ct, type, meth, helperParams, before, after, afterParams);
+        }
+    }
+
+    /**
+     * Proxy the given method with one that overrides it by calling into the
+     * given helper.
+     */
+    private void proxyOverrideMethod(ClassWriterTracker ct, Method meth, Method helper, Class[] helperParams) {
+        MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
+                Type.getMethodDescriptor(meth), null, null);
+        mv.visitCode();
+
+        // push all the method params to the stack
+        // we only start at param[1] as param[0] of the helper method is the instance itself
+        // and will get loaded with ALOAD_0 (this)
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        for (int i = 1; i < helperParams.length; i++)
+        {
+            mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
+        }
+
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(helper.getDeclaringClass()), helper.getName(),
+                Type.getMethodDescriptor(helper), false);
+
+        mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
+        mv.visitMaxs(-1, -1);
+        mv.visitEnd();
+    }
+
+    /**
+     * Proxy the given method with one that overrides it by calling into the
+     * given helper.
+     */
+    private void proxyBeforeAfterMethod(ClassWriterTracker ct, Class type, Method meth, Class[] helperParams,
+                                        Method before, Method after, Class[] afterParams) {
+
+        MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
+                Type.getMethodDescriptor(meth), null, null);
+        mv.visitCode();
+
+        int beforeRetPos = -1;
+        int variableNr = helperParams.length;;
+
+        // invoke before
+        if (before != null) {
+            // push all the method params to the stack
+            // we only start at param[1] as param[0] of the helper method is the instance itself
+            // and will get loaded with ALOAD_0 (this)
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            for (int i = 1; i < helperParams.length; i++)
+            {
+                mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
+            }
+
+            mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(before.getDeclaringClass()), before.getName(),
+                    Type.getMethodDescriptor(before), false);
+
+            if (after != null && before.getReturnType() != void.class) {
+                // this is always a boolean and 1 after the
+                beforeRetPos = variableNr++;
+                mv.visitVarInsn(AsmHelper.getStoreInsn(before.getReturnType()), beforeRetPos);
+            }
+        }
+
+        // invoke super
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        for (int i = 1; i < helperParams.length; i++)
+        {
+            mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
+        }
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
+                Type.getMethodDescriptor(meth), false);
+
+        // invoke after
+        if (after != null) {
+            int retPos = -1;
+            if (meth.getReturnType() != void.class) {
+                retPos = variableNr++;
+                mv.visitVarInsn(AsmHelper.getStoreInsn(meth.getReturnType()), retPos);
+            }
+
+            // push all the method params to the stack
+            // we only start at param[1] as param[0] of the helper method is the instance itself
+            // and will get loaded with ALOAD_0 (this)
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            for (int i = 1; i < helperParams.length; i++)
+            {
+                mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
+            }
+
+            if (retPos != -1) {
+                mv.visitVarInsn(AsmHelper.getLoadInsn(meth.getReturnType()),retPos);
+            }
+            if (beforeRetPos != -1) {
+                mv.visitVarInsn(AsmHelper.getLoadInsn(before.getReturnType()),beforeRetPos);
+            }
+            mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(after.getDeclaringClass()), after.getName(),
+                    Type.getMethodDescriptor(after), false);
+        }
+
+        mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
+        mv.visitMaxs(-1, -1);
+        mv.visitEnd();
     }
 
 
     /**
      * add the instance variables to the class to be generated
      */
-    private void addInstanceVariables(ClassWriter cw) {
+    private void addInstanceVariables(ClassWriterTracker ct) {
         // variable #1, the state manager
-        cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
+        ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
                 "sm", Type.getDescriptor(OpenJPAStateManager.class), null, null).visitEnd();
 
         // variable #2, the state manager
-        cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
+        ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
                 "field", Type.getDescriptor(int.class), null, null).visitEnd();
     }
 
@@ -754,19 +1084,19 @@ public class ProxyManagerImpl
     /**
      * Create pass-through constructors to base type.
      */
-    private void delegateConstructors(ClassWriter cw, Class type, String superClassFileNname) {
+    private void delegateConstructors(ClassWriterTracker ct, Class type, String superClassFileNname) {
         Constructor[] constructors = type.getConstructors();
 
         for (Constructor constructor : constructors) {
             Class[] params = constructor.getParameterTypes();
-            String[] exceptionTypes = getInternalNames(constructor.getExceptionTypes());
+            String[] exceptionTypes = AsmHelper.getInternalNames(constructor.getExceptionTypes());
             String descriptor = Type.getConstructorDescriptor(constructor);
-            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
+            MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
             mv.visitCode();
             mv.visitVarInsn(Opcodes.ALOAD, 0);
             for (int i = 1; i <= params.length; i++)
             {
-                mv.visitVarInsn(getVarInsn(params[i-1]), i);
+                mv.visitVarInsn(AsmHelper.getLoadInsn(params[i-1]), i);
             }
             mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassFileNname, "<init>", descriptor, false);
 
@@ -780,14 +1110,14 @@ public class ProxyManagerImpl
      * 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
+     * @param defaultChangeTracker whether to implement a null change tracker; if false
      * the change tracker method is left unimplemented
      * @param proxyClassDef
      */
-    private void addProxyMethods(ClassWriter cw, boolean changeTracker, String proxyClassDef, Class<?> parentClass) {
+    private void addProxyMethods(ClassWriterTracker ct, boolean defaultChangeTracker, String proxyClassDef, Class<?> parentClass) {
 
         {
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "setOwner",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "setOwner",
                     Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE)
                     , null, null);
             mv.visitCode();
@@ -805,7 +1135,7 @@ public class ProxyManagerImpl
         }
 
         {
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwner",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwner",
                     Type.getMethodDescriptor(Type.getType(OpenJPAStateManager.class))
                     , null, null);
             mv.visitCode();
@@ -819,7 +1149,7 @@ public class ProxyManagerImpl
         }
 
         {
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwnerField",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwnerField",
                     Type.getMethodDescriptor(Type.INT_TYPE)
                     , null, null);
             mv.visitCode();
@@ -840,7 +1170,7 @@ public class ProxyManagerImpl
              * was invoked.  So, we are now overriding the clone() method so as to
              * provide a detached proxy object (null out the StateManager).
              */
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "clone",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "clone",
                     Type.getMethodDescriptor(TYPE_OBJECT)
                     , null, null);
             mv.visitCode();
@@ -863,8 +1193,8 @@ public class ProxyManagerImpl
             mv.visitEnd();
         }
 
-        if (changeTracker) {
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getChangeTracker",
+        if (defaultChangeTracker) {
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
                     Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
                     , null, null);
             mv.visitCode();
@@ -878,7 +1208,7 @@ public class ProxyManagerImpl
     /**
      * Implement the methods in the {@link ProxyDate} interface.
      */
-    private void addProxyDateMethods(ClassWriter cw, String proxyClassDef, Class type) {
+    private void addProxyDateMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
 
         final boolean hasDefaultCons = hasConstructor(type);
         final boolean hasMillisCons = hasConstructor(type, long.class);
@@ -889,7 +1219,7 @@ public class ProxyManagerImpl
 
         // add a default constructor that delegates to the millis constructor
         if (!hasDefaultCons) {
-            MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
+            MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                     Type.getMethodDescriptor(Type.VOID_TYPE), null, null);
             mv.visitCode();
             mv.visitVarInsn(Opcodes.ALOAD, 0);
@@ -918,7 +1248,7 @@ public class ProxyManagerImpl
                 params = new Class[0];
             }
 
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
                     Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
                     , null, null);
             mv.visitCode();
@@ -926,21 +1256,20 @@ public class ProxyManagerImpl
             mv.visitInsn(Opcodes.DUP);
 
             if (params.length == 1) {
+                mv.visitVarInsn(Opcodes.ALOAD, 1);
                 if (params[0] == long.class) {
                     // call getTime on the given Date if the current type has a long constructor
-                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                     mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class));
                     mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime",
                             Type.getMethodDescriptor(Type.LONG_TYPE), false);
                 }
                 else {
                     // otherwise just pass the parameter
-                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                     mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
                 }
             }
             mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>",
-                    Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false);
+                    Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
 
             if (params.length == 0) {
                 mv.visitInsn(Opcodes.DUP);
@@ -970,7 +1299,7 @@ public class ProxyManagerImpl
 
         {
             // new instance factory
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
                     Type.getMethodDescriptor(Type.getType(ProxyDate.class))
                     , null, null);
             mv.visitCode();
@@ -985,13 +1314,13 @@ public class ProxyManagerImpl
         }
     }
 
-    private void addProxyCalendarMethods(ClassWriter cw, String proxyClassDef, Class type) {
+    private void addProxyCalendarMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
         // calendar copy
         {
             Constructor cons = findCopyConstructor(type);
             Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
 
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
                     Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
                     , null, null);
             mv.visitCode();
@@ -999,7 +1328,7 @@ public class ProxyManagerImpl
             mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
             mv.visitInsn(Opcodes.DUP);
             mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>",
-                    Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false);
+                    Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
 
             // timeInMillis
             mv.visitInsn(Opcodes.DUP);
@@ -1053,7 +1382,7 @@ public class ProxyManagerImpl
 
         // newInstance factory
         {
-            MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
+            MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
                     Type.getMethodDescriptor(Type.getType(ProxyCalendar.class))
                     , null, null);
             mv.visitCode();
@@ -1070,7 +1399,7 @@ public class ProxyManagerImpl
         // proxy the protected computeFields method b/c it is called on
         // mutate, and some setters are final and therefore not proxyable
         {
-            MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "computeFields",
+            MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "computeFields",
                     Type.getMethodDescriptor(Type.VOID_TYPE)
                     , null, null);
             mv.visitCode();
@@ -1088,25 +1417,6 @@ public class ProxyManagerImpl
             mv.visitMaxs(-1, -1);
             mv.visitEnd();
         }
-
-
-        /*
-
-        // proxy the protected computeFields method b/c it is called on
-        // mutate, and some setters are final and therefore not proxyable
-        m = bc.declareMethod("computeFields", void.class, null);
-        m.makeProtected();
-        code = m.getCode(true);
-        code.aload().setThis();
-        code.constant().setValue(true);
-        code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
-            new Class[] { Proxy.class, boolean.class });
-        code.aload().setThis();
-        code.invokespecial().setMethod(type, "computeFields", void.class, null);
-        code.vreturn();
-        code.calculateMaxStack();
-        code.calculateMaxLocals();
-         */
     }
 
     /**
@@ -1114,28 +1424,30 @@ public class ProxyManagerImpl
      *
      * @return true if we generated any setters, false otherwise
      */
-    private boolean proxySetters(ClassWriter cw, String proxyClassDef, Class type) {
+    private boolean proxySetters(ClassWriterTracker ct, String proxyClassDef, Class type) {
         Method[] meths = type.getMethods();
 
         int setters = 0;
         for (Method meth : meths) {
             if (isSetter(meth) && !Modifier.isFinal(meth.getModifiers())) {
-
                 setters++;
-                proxySetter(cw, proxyClassDef, type, meth);
+                proxySetter(ct, proxyClassDef, type, meth);
             }
         }
         return setters > 0;
     }
 
-    private void proxySetter(ClassWriter cw, String proxyClassDef, Class type, Method meth) {
+    private void proxySetter(ClassWriterTracker ct, String proxyClassDef, Class type, Method meth) {
         Class[] params = meth.getParameterTypes();
         Class ret = meth.getReturnType();
 
-        final String methodDescriptor = Type.getMethodDescriptor(Type.getType(ret), getParamTypes(params));
-        MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, meth.getName(),
-                methodDescriptor
-                , null, null);
+        final String methodDescriptor = Type.getMethodDescriptor(Type.getType(ret), AsmHelper.getParamTypes(params));
+        if (ct.hasMethod(meth.getName(), methodDescriptor)) {
+            // this method already got created
+            return;
+        }
+
+        MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, meth.getName(), methodDescriptor, null, null);
         mv.visitCode();
         mv.visitVarInsn(Opcodes.ALOAD, 0);
         mv.visitInsn(Opcodes.ICONST_1);
@@ -1147,13 +1459,13 @@ public class ProxyManagerImpl
         // push all the method params to the stack
         for (int i = 1; i <= params.length; i++)
         {
-            mv.visitVarInsn(getVarInsn(params[i-1]), i);
+            mv.visitVarInsn(AsmHelper.getLoadInsn(params[i-1]), i);
         }
 
         mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
                 methodDescriptor, false);
 
-        mv.visitInsn(getReturnInsn(ret));
+        mv.visitInsn(AsmHelper.getReturnInsn(ret));
         mv.visitMaxs(-1, -1);
         mv.visitEnd();
     }
@@ -1163,8 +1475,8 @@ public class ProxyManagerImpl
      * Add a writeReplace implementation that serializes to a non-proxy type
      * unless detached and this is a build-time generated class.
      */
-    private void addWriteReplaceMethod(ClassWriter cw, String proxyClassDef, boolean runtime) {
-        MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "writeReplace",
+    private void addWriteReplaceMethod(ClassWriterTracker ct, String proxyClassDef, boolean runtime) {
+        MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "writeReplace",
                 Type.getMethodDescriptor(TYPE_OBJECT)
                 , null, new String[]{Type.getInternalName(ObjectStreamException.class)});
         mv.visitCode();
@@ -1180,23 +1492,7 @@ public class ProxyManagerImpl
 
     /* a few utility methods to make life with ASM easier */
 
-    private String[] getInternalNames(Class[] classes) {
-        String[] internalNames = new String[classes.length];
 
-        for (int i=0; i<classes.length; i++) {
-            internalNames[i] = Type.getInternalName(classes[i]);
-        }
-        return internalNames;
-    }
-
-    private Type[] getParamTypes(Class[] params) {
-        Type[] types = new Type[params.length];
-        for (int i=0; i<types.length; i++) {
-            types[i] = Type.getType(params[i]);
-        }
-
-        return types;
-    }
 
     private boolean hasConstructor(Class type, Class<?>... paramTypes) {
         try {
@@ -1207,76 +1503,6 @@ public class ProxyManagerImpl
         }
     }
 
-    /**
-     * Returns the appropriate bytecode instruction to load a value from a variable to the stack
-     *
-     * @param type Type to load
-     * @return Bytecode instruction to use
-     */
-    private int getVarInsn(Class<?> type)
-    {
-        if (type.isPrimitive())
-        {
-            if (Integer.TYPE.equals(type))
-            {
-                return Opcodes.ILOAD;
-            }
-            else if (Boolean.TYPE.equals(type))
-            {
-                return Opcodes.ILOAD;
-            }
-            else if (Character.TYPE.equals(type))
-            {
-                return Opcodes.ILOAD;
-            }
-            else if (Byte.TYPE.equals(type))
-            {
-                return Opcodes.ILOAD;
-            }
-            else if (Short.TYPE.equals(type))
-            {
-                return Opcodes.ILOAD;
-            }
-            else if (Float.TYPE.equals(type))
-            {
-                return Opcodes.FLOAD;
-            }
-            else if (Long.TYPE.equals(type))
-            {
-                return Opcodes.LLOAD;
-            }
-            else if (Double.TYPE.equals(type))
-            {
-                return Opcodes.DLOAD;
-            }
-        }
-
-        return Opcodes.ALOAD;
-    }
-
-    /**
-     * calclates the proper Return instruction opcode for the given class
-     */
-    private int getReturnInsn(Class ret) {
-        if (ret.equals(Void.TYPE)) {
-            return Opcodes.RETURN;
-        }
-        if (ret.equals(Integer.TYPE)) {
-            return Opcodes.IRETURN;
-        }
-        if (ret.equals(Long.TYPE)) {
-            return Opcodes.LRETURN;
-        }
-        if (ret.equals(Float.TYPE)) {
-            return Opcodes.FRETURN;
-        }
-        if (ret.equals(Double.TYPE)) {
-            return Opcodes.DRETURN;
-        }
-        return Opcodes.ARETURN;
-    }
-
-
     /* ASM end */
 
 
@@ -1663,7 +1889,7 @@ public class ProxyManagerImpl
         code.calculateMaxStack();
         code.calculateMaxLocals();
     }
-    
+
     /**
      * Implement the methods in the {@link ProxyBean} interface.
      */
@@ -2142,16 +2368,22 @@ public class ProxyManagerImpl
 
             // ASM generated proxies
             if (Date.class.isAssignableFrom(cls) ||
-                Calendar.class.isAssignableFrom(cls)) {
+                Calendar.class.isAssignableFrom(cls) ||
+                Collection.class.isAssignableFrom(cls)) {
                 final String proxyClassName = getProxyClassName(cls, false);
 
                 byte[] bytes = null;
+
                 if (Date.class.isAssignableFrom(cls)) {
                     bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName);
                 }
                 else if (Calendar.class.isAssignableFrom(cls)) {
                     bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName);
                 }
+                else if (Collection.class.isAssignableFrom(cls)) {
+                    bytes = mgr.generateProxyCollectionBytecode(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);
@@ -2159,8 +2391,6 @@ public class ProxyManagerImpl
                 continue;
             }
 
-            if (Collection.class.isAssignableFrom(cls))
-                bc = mgr.generateProxyCollectionBytecode(cls, false);
             else if (Map.class.isAssignableFrom(cls))
                 bc = mgr.generateProxyMapBytecode(cls, false);
             else {
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
new file mode 100644
index 000000000..ec84023a3
--- /dev/null
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/asm/AsmHelper.java
@@ -0,0 +1,182 @@
+/*
+ * 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.asm;
+
+import java.lang.reflect.Method;
+
+import org.apache.xbean.asm9.Opcodes;
+import org.apache.xbean.asm9.Type;
+
+/**
+ * Utility methods to deal with ASM bytecode
+ * 
+ * @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
+ */
+public final class AsmHelper {
+    private AsmHelper() {
+        // utility class ct
+    }
+
+    /**
+     * Calclates the proper Return instruction opcode for the given class
+     * 
+     * @param type the type to get returned
+     * @return the proper Opcode RETURN, ARETURN, IRETURN, etc
+     */
+    public static int getReturnInsn(Class type) {
+        if (type.equals(Void.TYPE)) {
+            return Opcodes.RETURN;
+        }
+        if (type.isPrimitive()) {
+            if (Integer.TYPE.equals(type)) {
+                return Opcodes.IRETURN;
+            }
+            else if (Boolean.TYPE.equals(type)) {
+                return Opcodes.IRETURN;
+            }
+            else if (Character.TYPE.equals(type)) {
+                return Opcodes.IRETURN;
+            }
+            else if (Byte.TYPE.equals(type)) {
+                return Opcodes.IRETURN;
+            }
+            else if (Short.TYPE.equals(type)) {
+                return Opcodes.IRETURN;
+            }
+            else if (Float.TYPE.equals(type)) {
+                return Opcodes.FRETURN;
+            }
+            else if (Long.TYPE.equals(type)) {
+                return Opcodes.LRETURN;
+            }
+            else if (Double.TYPE.equals(type)) {
+                return Opcodes.DRETURN;
+            }
+        }
+        return Opcodes.ARETURN;
+    }
+
+    /**
+     * Calclates the proper STORE instruction opcode for the given type
+     *
+     * @param type the type to get stored
+     * @return the proper Opcode ISTORE, ASTORE, LSTORE, etc
+     */
+    public static int getStoreInsn(Class<?> type) {
+        if (type.equals(Void.TYPE)) {
+            throw new IllegalArgumentException("Void type cannot be stored");
+        }
+        if (type.isPrimitive()) {
+            if (Integer.TYPE.equals(type)) {
+                return Opcodes.ISTORE;
+            }
+            else if (Boolean.TYPE.equals(type)) {
+                return Opcodes.ISTORE;
+            }
+            else if (Character.TYPE.equals(type)) {
+                return Opcodes.ISTORE;
+            }
+            else if (Byte.TYPE.equals(type)) {
+                return Opcodes.ISTORE;
+            }
+            else if (Short.TYPE.equals(type)) {
+                return Opcodes.ISTORE;
+            }
+            else if (Float.TYPE.equals(type)) {
+                return Opcodes.FSTORE;
+            }
+            else if (Long.TYPE.equals(type)) {
+                return Opcodes.LSTORE;
+            }
+            else if (Double.TYPE.equals(type)) {
+                return Opcodes.DSTORE;
+            }
+        }
+
+        return Opcodes.ASTORE;
+    }
+
+
+    /**
+     * Calclates the proper LOAD instruction opcode for the given type.
+     * This is the appropriate bytecode instruction to load a value from a variable to the stack.
+     *
+     * @param type the type to get loaded
+     * @return the proper Opcode ILOAD, ALOAD, LLOAD, etc
+     */
+    public static int getLoadInsn(Class<?> type) {
+        if (type.equals(Void.TYPE)) {
+            throw new IllegalArgumentException("Void type cannot be loaded");
+        }
+        if (type.isPrimitive()) {
+            if (Integer.TYPE.equals(type)) {
+                return Opcodes.ILOAD;
+            }
+            else if (Boolean.TYPE.equals(type)) {
+                return Opcodes.ILOAD;
+            }
+            else if (Character.TYPE.equals(type)) {
+                return Opcodes.ILOAD;
+            }
+            else if (Byte.TYPE.equals(type)) {
+                return Opcodes.ILOAD;
+            }
+            else if (Short.TYPE.equals(type)) {
+                return Opcodes.ILOAD;
+            }
+            else if (Float.TYPE.equals(type)) {
+                return Opcodes.FLOAD;
+            }
+            else if (Long.TYPE.equals(type)) {
+                return Opcodes.LLOAD;
+            }
+            else if (Double.TYPE.equals(type)) {
+                return Opcodes.DLOAD;
+            }
+        }
+
+        return Opcodes.ALOAD;
+    }
+
+    /**
+     * Get the internal names for the given classes
+     * @see Type#getInternalName(Class) 
+     */
+    public static String[] getInternalNames(Class[] classes) {
+        String[] internalNames = new String[classes.length];
+
+        for (int i=0; i<classes.length; i++) {
+            internalNames[i] = Type.getInternalName(classes[i]);
+        }
+        return internalNames;
+    }
+
+
+    /**
+     * get the ASM Types for the given classes
+     * @see Type#getType(Method)
+     */
+    public static Type[] getParamTypes(Class[] params) {
+        Type[] types = new Type[params.length];
+        for (int i=0; i<types.length; i++) {
+            types[i] = Type.getType(params[i]);
+        }
+
+        return types;
+    }
+
+}