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>