use asm commons to drastically shrink code; eliminate Unsafe class that relies on Sun/Oracle private APIs; incidentally fix a bug where caught exceptions had to match declared type exactly, with no notion of inheritance, to be correctly rethrown

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/proxy/branches/version-2.0-work@1521303 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/asm4/pom.xml b/asm4/pom.xml
index f0dff48..a17ea29 100644
--- a/asm4/pom.xml
+++ b/asm4/pom.xml
@@ -35,8 +35,12 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.xbean</groupId>
-            <artifactId>xbean-asm4-shaded</artifactId>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.ow2.asm</groupId>
+          <artifactId>asm-commons</artifactId>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
diff --git a/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java b/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java
index 8ad664a..1428391 100644
--- a/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java
+++ b/asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java
@@ -16,6 +16,14 @@
  */
 package org.apache.commons.proxy2.asm4;
 
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.proxy2.Interceptor;
 import org.apache.commons.proxy2.Invocation;
 import org.apache.commons.proxy2.Invoker;
@@ -25,22 +33,11 @@
 import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator;
 import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory;
 import org.apache.commons.proxy2.impl.ProxyClassCache;
-import org.apache.xbean.asm4.ClassWriter;
-import org.apache.xbean.asm4.Label;
-import org.apache.xbean.asm4.MethodVisitor;
-import org.apache.xbean.asm4.Opcodes;
-import org.apache.xbean.asm4.Type;
-
-import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.ProtectionDomain;
-import java.util.concurrent.atomic.AtomicInteger;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
 
 public class ASM4ProxyFactory extends AbstractSubclassingProxyFactory
 {
@@ -64,14 +61,20 @@
         return createProxy(classLoader, new InvokerInvocationHandler(invoker), proxyClasses);
     }
 
-    private <T> T createProxy(final ClassLoader classLoader, InvocationHandler invocationHandler, final Class<?>... proxyClasses)
+    private <T> T createProxy(final ClassLoader classLoader, final InvocationHandler invocationHandler, final Class<?>... proxyClasses)
     {
-    	final Class<?> proxyClass = PROXY_CLASS_CACHE.getProxyClass(classLoader, proxyClasses);
-    	final Object instance = Unsafe.allocateInstance(proxyClass);
-		Unsafe.setValue(ProxyGenerator.getDeclaredField(proxyClass, ProxyGenerator.HANDLER_NAME), instance, invocationHandler);
-		@SuppressWarnings("unchecked")
-		final T result = (T) instance;
-		return result;
+        final Class<?> proxyClass = PROXY_CLASS_CACHE.getProxyClass(
+                classLoader, proxyClasses);
+        try
+        {
+            @SuppressWarnings("unchecked")
+            final T result = (T) proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
+            return result;
+        }
+        catch (Exception e)
+        {
+            throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
+        }
     }
 
     private static class ProxyGenerator extends AbstractProxyClassGenerator implements Opcodes
@@ -79,6 +82,7 @@
         private static final AtomicInteger CLASS_NUMBER = new AtomicInteger(0);
         private static final String CLASSNAME_PREFIX = "CommonsProxyASM4_";
         private static final String HANDLER_NAME = "__handler";
+        private static final Type HANDLER_TYPE = Type.getType(InvocationHandler.class);
 
         @Override
         public Class<?> generateProxyClass(final ClassLoader classLoader, final Class<?>... proxyClasses)
@@ -92,7 +96,7 @@
 			try
 			{
 			    final byte[] proxyBytes = generateProxy(superclass, classFileName, implementationMethods, interfaces);
-			    return Unsafe.defineClass(classLoader, superclass, proxyName, proxyBytes);
+			    return loadClass(classLoader, proxyName, proxyBytes);
 			}
 			catch (final Exception e)
 			{
@@ -100,51 +104,55 @@
 			}
         }
 
-        private static Field getDeclaredField(final Class<?> clazz, final String fieldName)
-        {
-            try
-            {
-                return clazz.getDeclaredField(fieldName);
-            }
-            catch (NoSuchFieldException e)
-            {
-                final String message = String.format("Proxy class does not contain expected field \"%s\": %s", fieldName, clazz.getName());
-                throw new IllegalStateException(message, e);
-            }
-        }
-
         private static byte[] generateProxy(final Class<?> classToProxy, final String proxyName, final Method[] methods, final Class<?>... interfaces) throws ProxyFactoryException
         {
             final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 
-            final String proxyClassFileName = proxyName.replace('.', '/');
-            final String classFileName = classToProxy.getName().replace('.', '/');
+            final Type proxyType = Type.getObjectType(proxyName);
 
             // push class signature
             final String[] interfaceNames = new String[interfaces.length];
             for (int i = 0; i < interfaces.length; i++)
             {
-                final Class<?> anInterface = interfaces[i];
-                interfaceNames[i] = anInterface.getName().replace('.', '/');
+                interfaceNames[i] = Type.getType(interfaces[i]).getInternalName();
             }
 
-            cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, proxyClassFileName, null, classFileName, interfaceNames);
+            final Type superType = Type.getType(classToProxy);
+            cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, proxyType.getInternalName(), null, superType.getInternalName(), interfaceNames);
 
             // push InvocationHandler fields
-            cw.visitField(ACC_FINAL + ACC_PRIVATE, HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null).visitEnd();
+            cw.visitField(ACC_FINAL + ACC_PRIVATE, HANDLER_NAME, HANDLER_TYPE.getDescriptor(), null, null).visitEnd();
+
+            init(cw, proxyType, superType);
 
             for (final Method method : methods)
             {
-                processMethod(cw, method, proxyClassFileName, HANDLER_NAME);
+                processMethod(cw, method, proxyType, HANDLER_NAME);
             }
 
             return cw.toByteArray();
         }
 
-        private static void processMethod(final ClassWriter cw, final Method method, final String proxyName, final String handlerName) throws ProxyFactoryException {
-            final Class<?> returnType = method.getReturnType();
-            final Class<?>[] parameterTypes = method.getParameterTypes();
-            final Class<?>[] exceptionTypes = method.getExceptionTypes();
+        private static void init(final ClassWriter cw, final Type proxyType, Type superType)
+        {
+            final GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, new org.objectweb.asm.commons.Method("<init>",
+                    Type.VOID_TYPE, new Type[] { HANDLER_TYPE }), null, null, cw);
+            // invoke super constructor:
+            mg.loadThis();
+            mg.invokeConstructor(superType, org.objectweb.asm.commons.Method.getMethod("void <init> ()"));
+
+            // assign handler:
+            mg.loadThis();
+            mg.loadArg(0);
+            mg.putField(proxyType, HANDLER_NAME, HANDLER_TYPE);
+            mg.returnValue();
+            mg.endMethod();
+        }
+
+        private static void processMethod(final ClassWriter cw, final Method method, final Type proxyType, final String handlerName) throws ProxyFactoryException {
+            final Type sig = Type.getType(method);
+            final Type[] exceptionTypes = getTypes(method.getExceptionTypes());
+
             final int modifiers = method.getModifiers();
 
             // push the method definition
@@ -158,502 +166,170 @@
                 modifier = ACC_PROTECTED;
             }
 
-            final MethodVisitor mv = cw.visitMethod(modifier, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null);
-            mv.visitCode();
+            final org.objectweb.asm.commons.Method m = org.objectweb.asm.commons.Method.getMethod(method);
+            final GeneratorAdapter mg = new GeneratorAdapter(modifier, m, null, getTypes(method.getExceptionTypes()), cw);
 
-            // push try/catch block, to catch declared exceptions, and to catch java.lang.Throwable
-            final Label l0 = new Label();
-            final Label l1 = new Label();
-            final Label l2 = new Label();
+            final Label tryBlock = exceptionTypes.length > 0 ? mg.mark() : null;
 
-            if (exceptionTypes.length > 0)
-            {
-                mv.visitTryCatchBlock(l0, l1, l2, "java/lang/reflect/InvocationTargetException");
-            }
-
-            // push try code
-            mv.visitLabel(l0);
-            final String classNameToOverride = method.getDeclaringClass().getName().replace('.', '/');
-            mv.visitLdcInsn(Type.getType("L" + classNameToOverride + ";"));
+            mg.push(Type.getType(method.getDeclaringClass()));
 
             // the following code generates the bytecode for this line of Java:
             // Method method = <proxy>.class.getMethod("add", new Class[] { <array of function argument classes> });
 
             // get the method name to invoke, and push to stack
-            mv.visitLdcInsn(method.getName());
+
+            mg.push(method.getName());
 
             // create the Class[]
-            createArrayDefinition(mv, parameterTypes.length, Class.class);
-
-            int length = 1;
+            mg.push(sig.getArgumentTypes().length);
+            final Type classType = Type.getType(Class.class);
+            mg.newArray(classType);
 
             // push parameters into array
-            for (int i = 0; i < parameterTypes.length; i++)
+            for (int i = 0; i < sig.getArgumentTypes().length; i++)
             {
                 // keep copy of array on stack
-                mv.visitInsn(DUP);
+                mg.dup();
 
-                final Class<?> parameterType = parameterTypes[i];
-
-                // push number onto stack
-                pushIntOntoStack(mv, i);
-
-                if (parameterType.isPrimitive())
-                {
-                    final String wrapperType = getWrapperType(parameterType);
-                    mv.visitFieldInsn(GETSTATIC, wrapperType, "TYPE", "Ljava/lang/Class;");
-                } else {
-                    mv.visitLdcInsn(Type.getType(getAsmTypeAsString(parameterType, true)));
-                }
-
-                mv.visitInsn(AASTORE);
-
-                if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType))
-                {
-                    length += 2;
-                } else {
-                    length++;
-                }
+                // push index onto stack
+                mg.push(i);
+                mg.push(sig.getArgumentTypes()[i]);
+                mg.arrayStore(classType);
             }
 
             // invoke getMethod() with the method name and the array of types
-            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
-
+            mg.invokeVirtual(classType, org.objectweb.asm.commons.Method.getMethod("java.lang.reflect.Method getDeclaredMethod(String, Class[])"));
             // store the returned method for later
-            mv.visitVarInsn(ASTORE, length);
 
             // the following code generates bytecode equivalent to:
             // return ((<returntype>) invocationHandler.invoke(this, method, new Object[] { <function arguments }))[.<primitive>Value()];
 
-            final Label l4 = new Label();
-            mv.visitLabel(l4);
-            mv.visitVarInsn(ALOAD, 0);
+            mg.loadThis();
 
-            // get the invocationHandler field from this class
-            mv.visitFieldInsn(GETFIELD, proxyName, handlerName, "Ljava/lang/reflect/InvocationHandler;");
+            mg.getField(proxyType, handlerName, HANDLER_TYPE);
+            // put below method:
+            mg.swap();
 
             // we want to pass "this" in as the first parameter
-            mv.visitVarInsn(ALOAD, 0);
-
-            // and the method we fetched earlier
-            mv.visitVarInsn(ALOAD, length);
+            mg.loadThis();
+            // put below method:
+            mg.swap();
 
             // need to construct the array of objects passed in
 
             // create the Object[]
-            createArrayDefinition(mv, parameterTypes.length, Object.class);
+            mg.push(sig.getArgumentTypes().length);
+            final Type objectType = Type.getType(Object.class);
+            mg.newArray(objectType);
 
-            int index = 1;
             // push parameters into array
-            for (int i = 0; i < parameterTypes.length; i++)
+            for (int i = 0; i < sig.getArgumentTypes().length; i++)
             {
                 // keep copy of array on stack
-                mv.visitInsn(DUP);
+                mg.dup();
 
-                final Class<?> parameterType = parameterTypes[i];
+                // push index onto stack
+                mg.push(i);
 
-                // push number onto stack
-                pushIntOntoStack(mv, i);
-
-                if (parameterType.isPrimitive())
-                {
-                    final String wrapperType = getWrapperType(parameterType);
-                    mv.visitVarInsn(getVarInsn(parameterType), index);
-
-                    mv.visitMethodInsn(INVOKESTATIC, wrapperType, "valueOf", "(" + getPrimitiveLetter(parameterType) + ")L" + wrapperType + ";");
-                    mv.visitInsn(AASTORE);
-
-                    if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType))
-                    {
-                        index += 2;
-                    }
-                    else
-                    {
-                        index++;
-                    }
-                }
-                else
-                {
-                    mv.visitVarInsn(ALOAD, index);
-                    mv.visitInsn(AASTORE);
-                    index++;
-                }
+                mg.loadArg(i);
+                mg.valueOf(sig.getArgumentTypes()[i]);
+                mg.arrayStore(objectType);
             }
 
             // invoke the invocationHandler
-            mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+            mg.invokeInterface(HANDLER_TYPE, org.objectweb.asm.commons.Method.getMethod("Object invoke(Object, java.lang.reflect.Method, Object[])"));
 
             // cast the result
-            mv.visitTypeInsn(CHECKCAST, getCastType(returnType));
-
-            if (returnType.isPrimitive() && (!Void.TYPE.equals(returnType)))
-            {
-                // get the primitive value
-                mv.visitMethodInsn(INVOKEVIRTUAL, getWrapperType(returnType), getPrimitiveMethod(returnType), "()" + getPrimitiveLetter(returnType));
-            }
+            mg.unbox(sig.getReturnType());
 
             // push return
-            mv.visitLabel(l1);
-            if (!Void.TYPE.equals(returnType))
-            {
-                mv.visitInsn(getReturnInsn(returnType));
-            }
-            else
-            {
-                mv.visitInsn(POP);
-                mv.visitInsn(RETURN);
-            }
+            mg.returnValue();
 
             // catch InvocationTargetException
             if (exceptionTypes.length > 0)
             {
-                mv.visitLabel(l2);
-                mv.visitVarInsn(ASTORE, length);
+                final Type caughtExceptionType = Type.getType(InvocationTargetException.class);
+                mg.catchException(tryBlock, mg.mark(), caughtExceptionType);
 
-                final Label l5 = new Label();
-                mv.visitLabel(l5);
+                final Label throwCause = new Label();
 
+                mg.invokeVirtual(caughtExceptionType, org.objectweb.asm.commons.Method.getMethod("Throwable getCause()"));
+                
                 for (int i = 0; i < exceptionTypes.length; i++)
                 {
-                    final Class<?> exceptionType = exceptionTypes[i];
-
-                    mv.visitLdcInsn(Type.getType("L" + exceptionType.getName().replace('.', '/') + ";"));
-                    mv.visitVarInsn(ALOAD, length);
-                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;");
-                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
-                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
-
-                    final Label l6 = new Label();
-                    mv.visitJumpInsn(IFEQ, l6);
-
-                    final Label l7 = new Label();
-                    mv.visitLabel(l7);
-
-                    mv.visitVarInsn(ALOAD, length);
-                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;");
-                    mv.visitTypeInsn(CHECKCAST, exceptionType.getName().replace('.', '/'));
-                    mv.visitInsn(ATHROW);
-                    mv.visitLabel(l6);
-
-                    if (i == (exceptionTypes.length - 1))
-                    {
-                        mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
-                        mv.visitInsn(DUP);
-                        mv.visitVarInsn(ALOAD, length);
-                        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V");
-                        mv.visitInsn(ATHROW);
-                    }
+                    mg.dup();
+                    mg.push(exceptionTypes[i]);
+                    mg.swap();
+                    mg.invokeVirtual(classType, org.objectweb.asm.commons.Method.getMethod("boolean isInstance(Object)"));
+                    // if true, throw cause:
+                    mg.ifZCmp(GeneratorAdapter.NE, throwCause);
                 }
+                // no exception types matched; throw UndeclaredThrowableException:
+                final int cause = mg.newLocal(Type.getType(Exception.class));
+                mg.storeLocal(cause);
+                final Type undeclaredType = Type.getType(UndeclaredThrowableException.class);
+                mg.newInstance(undeclaredType);
+                mg.dup();
+                mg.loadLocal(cause);
+                mg.invokeConstructor(undeclaredType, new org.objectweb.asm.commons.Method("<init>", Type.VOID_TYPE,
+                        new Type[] { Type.getType(Throwable.class) }));
+                mg.throwException();
+
+                mg.mark(throwCause);
+                mg.throwException();
             }
 
             // finish this method
-            mv.visitMaxs(0, 0);
-            mv.visitEnd();
+            mg.endMethod();
         }
 
-        private static int getReturnInsn(final Class<?> type)
+        private static Type[] getTypes(Class<?>... src)
         {
-            if (type.isPrimitive())
+            final Type[] result = new Type[src.length];
+            for (int i = 0; i < result.length; i++)
             {
-                if (Integer.TYPE.equals(type))
-                {
-                    return IRETURN;
-                }
-                if (Boolean.TYPE.equals(type))
-                {
-                    return IRETURN;
-                }
-                if (Character.TYPE.equals(type))
-                {
-                    return IRETURN;
-                }
-                if (Byte.TYPE.equals(type))
-                {
-                    return IRETURN;
-                }
-                if (Short.TYPE.equals(type))
-                {
-                    return IRETURN;
-                }
-                if (Float.TYPE.equals(type))
-                {
-                    return FRETURN;
-                }
-                if (Long.TYPE.equals(type))
-                {
-                    return LRETURN;
-                }
-                if (Double.TYPE.equals(type))
-                {
-                    return DRETURN;
-                }
+                result[i] = Type.getType(src[i]);
             }
-
-            return ARETURN;
-        }
-
-        private static int getVarInsn(final Class<?> type)
-        {
-            if (type.isPrimitive())
-            {
-                if (Integer.TYPE.equals(type))
-                {
-                    return ILOAD;
-                }
-            	if (Boolean.TYPE.equals(type))
-            	{
-                    return ILOAD;
-                }
-            	if (Character.TYPE.equals(type))
-            	{
-                    return ILOAD;
-                }
-            	if (Byte.TYPE.equals(type))
-            	{
-                    return ILOAD;
-                }
-            	if (Short.TYPE.equals(type))
-            	{
-                    return ILOAD;
-                }
-            	if (Float.TYPE.equals(type))
-            	{
-                    return FLOAD;
-                }
-            	if (Long.TYPE.equals(type))
-            	{
-                    return LLOAD;
-                }
-            	if (Double.TYPE.equals(type))
-            	{
-                    return DLOAD;
-                }
-            }
-
-            throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
-        }
-
-        private static String getPrimitiveMethod(final Class<?> type)
-        {
-            if (Integer.TYPE.equals(type))
-            {
-                return "intValue";
-            }
-            if (Boolean.TYPE.equals(type))
-            {
-                return "booleanValue";
-            }
-            if (Character.TYPE.equals(type))
-            {
-                return "charValue";
-            }
-            if (Byte.TYPE.equals(type))
-            {
-                return "byteValue";
-            }
-            if (Short.TYPE.equals(type))
-            {
-                return "shortValue";
-            }
-            if (Float.TYPE.equals(type))
-            {
-                return "floatValue";
-            }
-            if (Long.TYPE.equals(type))
-            {
-                return "longValue";
-            }
-            if (Double.TYPE.equals(type))
-            {
-                return "doubleValue";
-            }
-
-            throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
-        }
-
-        private static String getCastType(final Class<?> returnType)
-        {
-            if (returnType.isPrimitive())
-            {
-                return getWrapperType(returnType);
-            }
-            return getAsmTypeAsString(returnType, false);
-        }
-
-        private static String getWrapperType(final Class<?> type)
-        {
-            if (Integer.TYPE.equals(type))
-            {
-                return Integer.class.getCanonicalName().replace('.', '/');
-            }
-            if (Boolean.TYPE.equals(type))
-            {
-                return Boolean.class.getCanonicalName().replace('.', '/');
-            }
-            if (Character.TYPE.equals(type))
-            {
-                return Character.class.getCanonicalName().replace('.', '/');
-            }
-            if (Byte.TYPE.equals(type))
-            {
-                return Byte.class.getCanonicalName().replace('.', '/');
-            }
-            if (Short.TYPE.equals(type))
-            {
-                return Short.class.getCanonicalName().replace('.', '/');
-            }
-            if (Float.TYPE.equals(type))
-            {
-                return Float.class.getCanonicalName().replace('.', '/');
-            }
-            if (Long.TYPE.equals(type))
-            {
-                return Long.class.getCanonicalName().replace('.', '/');
-            }
-            if (Double.TYPE.equals(type))
-            {
-                return Double.class.getCanonicalName().replace('.', '/');
-            }
-            if (Void.TYPE.equals(type))
-            {
-                return Void.class.getCanonicalName().replace('.', '/');
-            }
-
-            throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
-        }
-
-        private static void pushIntOntoStack(final MethodVisitor mv, final int i)
-        {
-            if (i == 0)
-            {
-                mv.visitInsn(ICONST_0);
-            }
-            else if (i == 1)
-            {
-                mv.visitInsn(ICONST_1);
-            }
-            else if (i == 2)
-            {
-                mv.visitInsn(ICONST_2);
-            }
-            else if (i == 3)
-            {
-                mv.visitInsn(ICONST_3);
-            }
-            else if (i == 4)
-            {
-                mv.visitInsn(ICONST_4);
-            }
-            else if (i == 5)
-            {
-                mv.visitInsn(ICONST_5);
-            }
-            else if (i > 5 && i <= 255)
-            {
-                mv.visitIntInsn(BIPUSH, i);
-            }
-            else
-            {
-                mv.visitIntInsn(SIPUSH, i);
-            }
-        }
-
-        private static void createArrayDefinition(final MethodVisitor mv, final int size, final Class<?> type) throws ProxyFactoryException
-        {
-            // create a new array of java.lang.class (2)
-            if (size < 0)
-            {
-                throw new ProxyFactoryException("Array size cannot be less than zero");
-            }
-
-            pushIntOntoStack(mv, size);
-            mv.visitTypeInsn(ANEWARRAY, type.getCanonicalName().replace('.', '/'));
-        }
-
-        private static String getMethodSignatureAsString(final Class<?> returnType, final Class<?>[] parameterTypes)
-        {
-            final StringBuilder builder = new StringBuilder("(");
-            for (final Class<?> parameterType : parameterTypes) {
-                builder.append(getAsmTypeAsString(parameterType, true));
-            }
-
-            builder.append(")");
-            builder.append(getAsmTypeAsString(returnType, true));
-
-            return builder.toString();
+            return result;
         }
 
         /**
-         * Returns the single letter that matches the given primitive in bytecode instructions
+         * Adapted from http://asm.ow2.org/doc/faq.html#Q5
+         * 
+         * @param b
+         * @return Class<?>
          */
-        private static String getPrimitiveLetter(final Class<?> type)
+        private static Class<?> loadClass(final ClassLoader loader, String className, byte[] b)
         {
-            if (Integer.TYPE.equals(type))
+            // override classDefine (as it is protected) and define the class.
+            try
             {
-                return "I";
-            }
-            if (Void.TYPE.equals(type))
-            {
-                return "V";
-            }
-            if (Boolean.TYPE.equals(type))
-            {
-                return "Z";
-            }
-            if (Character.TYPE.equals(type))
-            {
-                return "C";
-            }
-            if (Byte.TYPE.equals(type))
-            {
-                return "B";
-            }
-            if (Short.TYPE.equals(type))
-            {
-                return "S";
-            }
-        	if (Float.TYPE.equals(type))
-        	{
-                return "F";
-            }
-        	if (Long.TYPE.equals(type))
-        	{
-                return "J";
-            }
-        	if (Double.TYPE.equals(type))
-        	{
-                return "D";
-            }
+                final Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class,
+                        int.class, int.class);
 
-            throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
-        }
-
-        private static String getAsmTypeAsString(final Class<?> parameterType, final boolean wrap)
-        {
-            if (parameterType.isArray())
-            {
-                if (parameterType.getComponentType().isPrimitive())
+                // protected method invocation
+                final boolean accessible = method.isAccessible();
+                if (!accessible)
                 {
-                    final Class<?> componentType = parameterType.getComponentType();
-                    return "[" + getPrimitiveLetter(componentType);
+                    method.setAccessible(true);
                 }
-                return "[" + getAsmTypeAsString(parameterType.getComponentType(), true);
+                try
+                {
+                    return (Class<?>) method
+                            .invoke(loader, className, b, Integer.valueOf(0), Integer.valueOf(b.length));
+                }
+                finally
+                {
+                    if (!accessible)
+                    {
+                        method.setAccessible(false);
+                    }
+                }
             }
-            if (parameterType.isPrimitive()) {
-                return getPrimitiveLetter(parameterType);
-            }
-            String className = parameterType.getCanonicalName();
-
-            if (parameterType.isMemberClass()) {
-                final int lastDot = className.lastIndexOf(".");
-                className = className.substring(0, lastDot) + "$" + className.substring(lastDot + 1);
-            }
-
-            if (wrap)
+            catch (Exception e)
             {
-                return "L" + className.replace('.', '/') + ";";
+                throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e);
             }
-            return className.replace('.', '/');
         }
     }
 
@@ -793,177 +469,4 @@
             }
         }
     }
-
-	static class Unsafe
-	{
-	    // sun.misc.Unsafe
-	    private static final Object unsafe;
-	    private static final Method defineClass;
-	    private static final Method allocateInstance;
-	    private static final Method putObject;
-	    private static final Method objectFieldOffset;
-	
-	    static
-	    {
-	        final Class<?> unsafeClass;
-	        try {
-	            unsafeClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>()
-	    		{
-	                @Override
-	                public Class<?> run()
-	                {
-	                    try
-	                    {
-	                        return Thread.currentThread().getContextClassLoader().loadClass("sun.misc.Unsafe");
-	                    }
-	                    catch (Exception e)
-	                    {
-	                        try
-	                        {
-	                            return ClassLoader.getSystemClassLoader().loadClass("sun.misc.Unsafe");
-	                        }
-	                        catch (ClassNotFoundException e1)
-	                        {
-	                            throw new IllegalStateException("Cannot get sun.misc.Unsafe", e);
-	                        }
-	                    }
-	                }
-	            });
-	        }
-	        catch (Exception e)
-	        {
-	            throw new IllegalStateException("Cannot get sun.misc.Unsafe class", e);
-	        }
-	
-	        unsafe = AccessController.doPrivileged(new PrivilegedAction<Object>()
-			{
-	            @Override
-	            public Object run()
-	            {
-	                try
-	                {
-	                    final Field field = unsafeClass.getDeclaredField("theUnsafe");
-	                    field.setAccessible(true);
-	                    return field.get(null);
-	                }
-	                catch (Exception e)
-	                {
-	                    throw new IllegalStateException("Cannot get sun.misc.Unsafe", e);
-	                }
-	            }
-	        });
-	        allocateInstance = AccessController.doPrivileged(new PrivilegedAction<Method>()
-			{
-	            @Override
-	            public Method run()
-	            {
-	                try
-	                {
-	                    final Method mtd = unsafeClass.getDeclaredMethod("allocateInstance", Class.class);
-	                    mtd.setAccessible(true);
-	                    return mtd;
-	                }
-	                catch (Exception e)
-	                {
-	                    throw new IllegalStateException("Cannot get sun.misc.Unsafe.allocateInstance", e);
-	                }
-	            }
-	        });
-	        objectFieldOffset = AccessController.doPrivileged(new PrivilegedAction<Method>()
-			{
-	            @Override
-	            public Method run()
-	            {
-	                try
-	                {
-	                    final Method mtd = unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class);
-	                    mtd.setAccessible(true);
-	                    return mtd;
-	                }
-	                catch (Exception e)
-	                {
-	                    throw new IllegalStateException("Cannot get sun.misc.Unsafe.objectFieldOffset", e);
-	                }
-	            }
-	        });
-	        putObject = AccessController.doPrivileged(new PrivilegedAction<Method>()
-			{
-	            @Override
-	            public Method run()
-	            {
-	                try
-	                {
-	                    final Method mtd = unsafeClass.getDeclaredMethod("putObject", Object.class, long.class, Object.class);
-	                    mtd.setAccessible(true);
-	                    return mtd;
-	                }
-	                catch (Exception e)
-	                {
-	                    throw new IllegalStateException("Cannot get sun.misc.Unsafe.putObject", e);
-	                }
-	            }
-	        });
-	        defineClass = AccessController.doPrivileged(new PrivilegedAction<Method>()
-			{
-	            @Override
-	            public Method run()
-	            {
-	                try
-	                {
-	                    final Method mtd = unsafeClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
-	                    mtd.setAccessible(true);
-	                    return mtd;
-	                }
-	                catch (Exception e)
-	                {
-	                    throw new IllegalStateException("Cannot get sun.misc.Unsafe.defineClass", e);
-	                }
-	            }
-	        });
-	    }
-	
-	    static Object allocateInstance(final Class<?> clazz)
-	    {
-	        try
-	        {
-	            return allocateInstance.invoke(unsafe, clazz);
-	        }
-	        catch (IllegalAccessException e)
-	        {
-	            throw new IllegalStateException("Failed to allocateInstance of Proxy class " + clazz.getName(), e);
-	        }
-	        catch (InvocationTargetException e)
-	        {
-	            final Throwable throwable = e.getTargetException() != null ? e.getTargetException() : e;
-	            throw new IllegalStateException("Failed to allocateInstance of Proxy class " + clazz.getName(), throwable);
-	        }
-	    }
-	
-	    static void setValue(final Field field, final Object object, final Object value)
-	    {
-	        final long offset;
-	        try
-	        {
-	            offset = (Long) objectFieldOffset.invoke(unsafe, field);
-	        }
-	        catch (Exception e)
-	        {
-	            throw new IllegalStateException("Failed getting offset for: field=" + field.getName() + "  class=" + field.getDeclaringClass().getName(), e);
-	        }
-	
-	        try
-	        {
-	            putObject.invoke(unsafe, object, offset, value);
-	        }
-	        catch (Exception e)
-	        {
-	            throw new IllegalStateException("Failed putting field=" + field.getName() + "  class=" + field.getDeclaringClass().getName(), e);
-	        }
-	    }
-	
-	    static Class<?> defineClass(final ClassLoader loader, final Class<?> clsToProxy, final String proxyName, final byte[] proxyBytes) throws IllegalAccessException, InvocationTargetException
-	    {
-	        return (Class<?>) defineClass.invoke(unsafe, proxyName, proxyBytes, 0, proxyBytes.length, loader, clsToProxy.getProtectionDomain());
-	    }
-	}
 }
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 36d3e81..5a71852 100644
--- a/pom.xml
+++ b/pom.xml
@@ -217,9 +217,14 @@
                 <version>3.0</version>
             </dependency>
             <dependency>
-              <groupId>org.apache.xbean</groupId>
-              <artifactId>xbean-asm4-shaded</artifactId>
-              <version>3.14</version>
+              <groupId>org.ow2.asm</groupId>
+              <artifactId>asm</artifactId>
+              <version>${asm.version}</version>
+            </dependency>
+            <dependency>
+              <groupId>org.ow2.asm</groupId>
+              <artifactId>asm-commons</artifactId>
+              <version>${asm.version}</version>
             </dependency>
             <dependency>
                 <groupId>jmock</groupId>
@@ -242,5 +247,6 @@
         <commons.binary.suffix></commons.binary.suffix>
         <commons.jira.id>PROXY</commons.jira.id>
         <commons.jira.pid>12310731</commons.jira.pid>
+        <asm.version>4.1</asm.version>
     </properties>
 </project>