Last active
December 20, 2015 12:09
-
-
Save rmannibucau/6128964 to your computer and use it in GitHub Desktop.
asm4 module for commons-proxy2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: asm4/src/test/java/org/apache/commons/proxy2/javassist/TestAsm4ProxyFactory.java | |
=================================================================== | |
--- asm4/src/test/java/org/apache/commons/proxy2/javassist/TestAsm4ProxyFactory.java (revision 0) | |
+++ asm4/src/test/java/org/apache/commons/proxy2/javassist/TestAsm4ProxyFactory.java (working copy) | |
@@ -0,0 +1,10 @@ | |
+package org.apache.commons.proxy2.javassist; | |
+ | |
+import org.apache.commons.proxy2.AbstractSubclassingProxyFactoryTestCase; | |
+ | |
+public class TestAsm4ProxyFactory extends AbstractSubclassingProxyFactoryTestCase | |
+{ | |
+//********************************************************************************************************************** | |
+// Constructors | |
+//********************************************************************************************************************** | |
+} | |
Index: asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory | |
=================================================================== | |
--- asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory (revision 0) | |
+++ asm4/src/main/resources/META-INF/services/org.apache.commons.proxy2.ProxyFactory (working copy) | |
@@ -0,0 +1,17 @@ | |
+# | |
+# 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. | |
+# | |
+org.apache.commons.proxy2.asm4.ASM4ProxyFactory | |
Index: asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java | |
=================================================================== | |
--- asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java (revision 0) | |
+++ asm4/src/main/java/org/apache/commons/proxy2/asm4/ASM4ProxyFactory.java (working copy) | |
@@ -0,0 +1,832 @@ | |
+/* | |
+ * 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.commons.proxy2.asm4; | |
+ | |
+import org.apache.commons.proxy2.Interceptor; | |
+import org.apache.commons.proxy2.Invocation; | |
+import org.apache.commons.proxy2.Invoker; | |
+import org.apache.commons.proxy2.ObjectProvider; | |
+import org.apache.commons.proxy2.ProxyUtils; | |
+import org.apache.commons.proxy2.exception.ProxyFactoryException; | |
+import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator; | |
+import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory; | |
+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.lang.reflect.Proxy; | |
+import java.security.AccessController; | |
+import java.security.PrivilegedAction; | |
+import java.security.ProtectionDomain; | |
+import java.util.ArrayList; | |
+import java.util.Arrays; | |
+import java.util.HashMap; | |
+import java.util.List; | |
+import java.util.Map; | |
+import java.util.concurrent.locks.ReentrantLock; | |
+ | |
+public class ASM4ProxyFactory extends AbstractSubclassingProxyFactory { | |
+ @Override | |
+ public <T> T createDelegatorProxy(final ClassLoader classLoader, final ObjectProvider<?> delegateProvider, final Class<?>... proxyClasses) { | |
+ return ProxyGenerator.newProxyInstance(classLoader, new DelegatorInvocationHandler(delegateProvider), proxyClasses); | |
+ } | |
+ | |
+ @Override | |
+ public <T> T createInterceptorProxy(final ClassLoader classLoader, final Object target, final Interceptor interceptor, final Class<?>... proxyClasses) { | |
+ return ProxyGenerator.newProxyInstance(classLoader, new InterceptorInvocationHandler(target, interceptor), proxyClasses); | |
+ } | |
+ | |
+ @Override | |
+ public <T> T createInvokerProxy(final ClassLoader classLoader, final Invoker invoker, final Class<?>... proxyClasses) { | |
+ return ProxyGenerator.newProxyInstance(classLoader, new InvokerInvocationHandler(invoker), proxyClasses); | |
+ } | |
+ | |
+ private static class ProxyGenerator extends AbstractProxyClassGenerator implements Opcodes { | |
+ private static final String HANDLER_NAME = "__handler"; | |
+ private static final ReentrantLock LOCK = new ReentrantLock(); | |
+ | |
+ public static <T> T newProxyInstance(final ClassLoader classLoader, final InvocationHandler handler, final Class<?>... classes) throws ProxyFactoryException { | |
+ try { | |
+ final Class<?> superclass = getSuperclass(classes); | |
+ if (superclass == Object.class) { | |
+ return (T) Proxy.newProxyInstance(classLoader, classes, handler); | |
+ } | |
+ final Class proxyClass = createProxy(superclass, classLoader, getImplementationMethods(classes), toInterfaces(classes)); | |
+ return constructProxy(proxyClass, handler); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ } | |
+ | |
+ public static <T> T constructProxy(final Class clazz, final java.lang.reflect.InvocationHandler handler) throws IllegalStateException { | |
+ final Object instance = Unsafe.allocateInstance(clazz); | |
+ Unsafe.setValue(getDeclaredField(clazz, HANDLER_NAME), instance, handler); | |
+ return (T) instance; | |
+ } | |
+ | |
+ 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); | |
+ } | |
+ } | |
+ | |
+ public static Class createProxy(final Class<?> classToProxy, final ClassLoader loader, final String proxyName, final Method[] methods, final Class... interfaces) { | |
+ final String classFileName = proxyName.replace('.', '/'); | |
+ | |
+ try { | |
+ return loader.loadClass(proxyName); | |
+ } catch (Exception e) { | |
+ // no-op | |
+ } | |
+ | |
+ LOCK.lock(); | |
+ try { | |
+ | |
+ try { // Try it again, another thread may have beaten this one... | |
+ return loader.loadClass(proxyName); | |
+ } catch (Exception e) { | |
+ // no-op | |
+ } | |
+ | |
+ final byte[] proxyBytes = generateProxy(classToProxy, classFileName, methods, interfaces); | |
+ return Unsafe.defineClass(loader, classToProxy, proxyName, proxyBytes); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } finally { | |
+ LOCK.unlock(); | |
+ } | |
+ } | |
+ | |
+ public static Class createProxy(final Class<?> classToProxy, final ClassLoader cl, final Method[] methods, final Class... interfaces) { | |
+ return createProxy(classToProxy, cl, proxyName(classToProxy.getName()), methods, interfaces); | |
+ } | |
+ | |
+ private static String proxyName(final String name) { | |
+ final String proxy = name + "$$CommonsProxy"; | |
+ if (!proxy.startsWith("java")) { | |
+ return proxy; | |
+ } | |
+ return "org.apache.commons.proxy2.generated." + proxy; | |
+ } | |
+ | |
+ public 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('.', '/'); | |
+ | |
+ // 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('.', '/'); | |
+ } | |
+ | |
+ cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyClassFileName, null, classFileName, interfaceNames); | |
+ cw.visitSource(classFileName + ".java", null); | |
+ | |
+ // push InvocationHandler fields | |
+ cw.visitField(ACC_FINAL + ACC_PRIVATE, HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null).visitEnd(); | |
+ | |
+ final Map<String, List<Method>> methodMap = new HashMap<String, List<Method>>(); | |
+ | |
+ findMethods(classToProxy, methodMap); | |
+ | |
+ for (final Class<?> anInterface : interfaces) { | |
+ findMethods(anInterface, methodMap); | |
+ } | |
+ | |
+ for (final Method method : methods) { | |
+ processMethod(cw, method, proxyClassFileName, HANDLER_NAME); | |
+ } | |
+ | |
+ return cw.toByteArray(); | |
+ } | |
+ | |
+ private static void findMethods(Class<?> clazz, final Map<String, List<Method>> methodMap) { | |
+ while (clazz != null) { | |
+ for (final Method method : clazz.getDeclaredMethods()) { | |
+ final int modifiers = method.getModifiers(); | |
+ | |
+ if (Modifier.isFinal(modifiers) | |
+ || Modifier.isStatic(modifiers)) { | |
+ continue; | |
+ } | |
+ | |
+ List<Method> methods = methodMap.get(method.getName()); | |
+ if (methods == null) { | |
+ methods = new ArrayList<Method>(); | |
+ methods.add(method); | |
+ methodMap.put(method.getName(), methods); | |
+ } else { | |
+ if (!isOverridden(methods, method)) { | |
+ methods.add(method); | |
+ } | |
+ } | |
+ } | |
+ | |
+ clazz = clazz.getSuperclass(); | |
+ } | |
+ } | |
+ | |
+ private static boolean isOverridden(final List<Method> methods, final Method method) { | |
+ for (final Method m : methods) { | |
+ if (Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) { | |
+ return true; | |
+ } | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ private static void processMethod(final ClassWriter cw, final Method method, final String proxyName, final String handlerName) throws ProxyFactoryException { | |
+ if ("<init>".equals(method.getName())) { | |
+ return; | |
+ } | |
+ | |
+ final Class<?> returnType = method.getReturnType(); | |
+ final Class<?>[] parameterTypes = method.getParameterTypes(); | |
+ final Class<?>[] exceptionTypes = method.getExceptionTypes(); | |
+ final int modifiers = method.getModifiers(); | |
+ | |
+ // push the method definition | |
+ int modifier = 0; | |
+ if (Modifier.isPublic(modifiers)) { | |
+ modifier = ACC_PUBLIC; | |
+ } else if (Modifier.isProtected(modifiers)) { | |
+ modifier = ACC_PROTECTED; | |
+ } | |
+ | |
+ final MethodVisitor mv = cw.visitMethod(modifier, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null); | |
+ mv.visitCode(); | |
+ | |
+ // 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(); | |
+ | |
+ 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 + ";")); | |
+ | |
+ // 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()); | |
+ | |
+ // create the Class[] | |
+ createArrayDefinition(mv, parameterTypes.length, Class.class); | |
+ | |
+ int length = 1; | |
+ | |
+ // push parameters into array | |
+ for (int i = 0; i < parameterTypes.length; i++) { | |
+ // keep copy of array on stack | |
+ mv.visitInsn(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++; | |
+ } | |
+ } | |
+ | |
+ // 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;"); | |
+ | |
+ // 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); | |
+ | |
+ // get the invocationHandler field from this class | |
+ mv.visitFieldInsn(GETFIELD, proxyName, handlerName, "Ljava/lang/reflect/InvocationHandler;"); | |
+ | |
+ // we want to pass "this" in as the first parameter | |
+ mv.visitVarInsn(ALOAD, 0); | |
+ | |
+ // and the method we fetched earlier | |
+ mv.visitVarInsn(ALOAD, length); | |
+ | |
+ // need to construct the array of objects passed in | |
+ | |
+ // create the Object[] | |
+ createArrayDefinition(mv, parameterTypes.length, Object.class); | |
+ | |
+ int index = 1; | |
+ // push parameters into array | |
+ for (int i = 0; i < parameterTypes.length; i++) { | |
+ // keep copy of array on stack | |
+ mv.visitInsn(DUP); | |
+ | |
+ final Class<?> parameterType = parameterTypes[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++; | |
+ } | |
+ } | |
+ | |
+ // invoke the invocationHandler | |
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/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)); | |
+ } | |
+ | |
+ // push return | |
+ mv.visitLabel(l1); | |
+ if (!Void.TYPE.equals(returnType)) { | |
+ mv.visitInsn(getReturnInsn(returnType)); | |
+ } else { | |
+ mv.visitInsn(POP); | |
+ mv.visitInsn(RETURN); | |
+ } | |
+ | |
+ // catch InvocationTargetException | |
+ if (exceptionTypes.length > 0) { | |
+ mv.visitLabel(l2); | |
+ mv.visitVarInsn(ASTORE, length); | |
+ | |
+ final Label l5 = new Label(); | |
+ mv.visitLabel(l5); | |
+ | |
+ 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); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // finish this method | |
+ mv.visitMaxs(0, 0); | |
+ mv.visitEnd(); | |
+ } | |
+ | |
+ private static int getReturnInsn(final Class<?> type) { | |
+ if (type.isPrimitive()) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return IRETURN; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return IRETURN; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return IRETURN; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return IRETURN; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return IRETURN; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return FRETURN; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return LRETURN; | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return DRETURN; | |
+ } | |
+ } | |
+ | |
+ return ARETURN; | |
+ } | |
+ | |
+ private static int getVarInsn(final Class<?> type) { | |
+ if (type.isPrimitive()) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return ILOAD; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return ILOAD; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return ILOAD; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return ILOAD; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return ILOAD; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return FLOAD; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return LLOAD; | |
+ } else 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"; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return "booleanValue"; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return "charValue"; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return "byteValue"; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return "shortValue"; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return "floatValue"; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return "longValue"; | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return "doubleValue"; | |
+ } | |
+ | |
+ throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); | |
+ } | |
+ | |
+ static String getCastType(final Class<?> returnType) { | |
+ if (returnType.isPrimitive()) { | |
+ return getWrapperType(returnType); | |
+ } else { | |
+ return getAsmTypeAsString(returnType, false); | |
+ } | |
+ } | |
+ | |
+ private static String getWrapperType(final Class<?> type) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return Integer.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return Boolean.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return Character.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return Byte.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return Short.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return Float.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return Long.class.getCanonicalName().replace('.', '/'); | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return Double.class.getCanonicalName().replace('.', '/'); | |
+ } else 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('.', '/')); | |
+ } | |
+ | |
+ static String getMethodSignatureAsString(final Class<?> returnType, final Class<?>[] parameterTypes) { | |
+ final StringBuilder builder = new StringBuilder(); | |
+ builder.append("("); | |
+ for (final Class<?> parameterType : parameterTypes) { | |
+ builder.append(getAsmTypeAsString(parameterType, true)); | |
+ } | |
+ | |
+ builder.append(")"); | |
+ builder.append(getAsmTypeAsString(returnType, true)); | |
+ | |
+ return builder.toString(); | |
+ } | |
+ | |
+ /** | |
+ * Returns the single letter that matches the given primitive in bytecode instructions | |
+ */ | |
+ private static String getPrimitiveLetter(final Class<?> type) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return "I"; | |
+ } else if (Void.TYPE.equals(type)) { | |
+ return "V"; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return "Z"; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return "C"; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return "B"; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return "S"; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return "F"; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return "J"; | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return "D"; | |
+ } | |
+ | |
+ throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); | |
+ } | |
+ | |
+ public static String getAsmTypeAsString(final Class<?> parameterType, final boolean wrap) { | |
+ if (parameterType.isArray()) { | |
+ if (parameterType.getComponentType().isPrimitive()) { | |
+ final Class<?> componentType = parameterType.getComponentType(); | |
+ return "[" + getPrimitiveLetter(componentType); | |
+ } else { | |
+ return "[" + getAsmTypeAsString(parameterType.getComponentType(), true); | |
+ } | |
+ } else { | |
+ if (parameterType.isPrimitive()) { | |
+ return getPrimitiveLetter(parameterType); | |
+ } else { | |
+ String className = parameterType.getCanonicalName(); | |
+ | |
+ if (parameterType.isMemberClass()) { | |
+ final int lastDot = className.lastIndexOf("."); | |
+ className = className.substring(0, lastDot) + "$" + className.substring(lastDot + 1); | |
+ } | |
+ | |
+ if (wrap) { | |
+ return "L" + className.replace('.', '/') + ";"; | |
+ } else { | |
+ return className.replace('.', '/'); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ @Override // not used ATM | |
+ public Class<?> generateProxyClass(final ClassLoader classLoader, final Class<?>... proxyClasses) { | |
+ return createProxy(getSuperclass(proxyClasses), classLoader, getImplementationMethods(proxyClasses), toInterfaces(proxyClasses)); | |
+ } | |
+ | |
+ private 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); | |
+ } | |
+ } | |
+ }); | |
+ } | |
+ | |
+ private 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); | |
+ } | |
+ } | |
+ | |
+ private 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); | |
+ } | |
+ } | |
+ | |
+ private 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()); | |
+ } | |
+ } | |
+ } | |
+ | |
+ //////////////// these classes should be protected in ProxyFactory | |
+ private static class DelegatorInvocationHandler extends AbstractInvocationHandler { | |
+ private final ObjectProvider delegateProvider; | |
+ | |
+ protected DelegatorInvocationHandler(ObjectProvider delegateProvider) { | |
+ this.delegateProvider = delegateProvider; | |
+ } | |
+ | |
+ public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable { | |
+ try { | |
+ return method.invoke(delegateProvider.getObject(), args); | |
+ } catch (InvocationTargetException e) { | |
+ throw e.getTargetException(); | |
+ } | |
+ } | |
+ } | |
+ | |
+ private static class InterceptorInvocationHandler extends AbstractInvocationHandler { | |
+ private final Object target; | |
+ private final Interceptor methodInterceptor; | |
+ | |
+ public InterceptorInvocationHandler(Object target, Interceptor methodInterceptor) { | |
+ this.target = target; | |
+ this.methodInterceptor = methodInterceptor; | |
+ } | |
+ | |
+ public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable { | |
+ final ReflectionInvocation invocation = new ReflectionInvocation(target, proxy, method, args); | |
+ return methodInterceptor.intercept(invocation); | |
+ } | |
+ } | |
+ | |
+ private abstract static class AbstractInvocationHandler implements InvocationHandler, Serializable { | |
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
+ if (isHashCode(method)) { | |
+ return System.identityHashCode(proxy); | |
+ } else if (isEqualsMethod(method)) { | |
+ return proxy == args[0]; | |
+ } else { | |
+ return invokeImpl(proxy, method, args); | |
+ } | |
+ } | |
+ | |
+ protected abstract Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable; | |
+ } | |
+ | |
+ private static class InvokerInvocationHandler extends AbstractInvocationHandler { | |
+ private final Invoker invoker; | |
+ | |
+ public InvokerInvocationHandler(Invoker invoker) { | |
+ this.invoker = invoker; | |
+ } | |
+ | |
+ public Object invokeImpl(Object proxy, Method method, Object[] args) throws Throwable { | |
+ return invoker.invoke(proxy, method, args); | |
+ } | |
+ } | |
+ | |
+ protected static boolean isHashCode(Method method) { | |
+ return "hashCode".equals(method.getName()) && | |
+ Integer.TYPE.equals(method.getReturnType()) && | |
+ method.getParameterTypes().length == 0; | |
+ } | |
+ | |
+ protected static boolean isEqualsMethod(Method method) { | |
+ return "equals".equals(method.getName()) && | |
+ Boolean.TYPE.equals(method.getReturnType()) && | |
+ method.getParameterTypes().length == 1 && | |
+ Object.class.equals(method.getParameterTypes()[0]); | |
+ } | |
+ | |
+ private static class ReflectionInvocation implements Invocation, Serializable { | |
+ private final Method method; | |
+ private final Object[] arguments; | |
+ private final Object proxy; | |
+ private final Object target; | |
+ | |
+ public ReflectionInvocation(final Object target, final Object proxy, final Method method, final Object[] arguments) { | |
+ this.method = method; | |
+ this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments); | |
+ this.proxy = proxy; | |
+ this.target = target; | |
+ } | |
+ | |
+ public Object[] getArguments() { | |
+ return arguments; | |
+ } | |
+ | |
+ public Method getMethod() { | |
+ return method; | |
+ } | |
+ | |
+ public Object getProxy() { | |
+ return proxy; | |
+ } | |
+ | |
+ public Object proceed() throws Throwable { | |
+ try { | |
+ return method.invoke(target, arguments); | |
+ } catch (InvocationTargetException e) { | |
+ throw e.getTargetException(); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
Index: asm4/pom.xml | |
=================================================================== | |
--- asm4/pom.xml (revision 0) | |
+++ asm4/pom.xml (working copy) | |
@@ -0,0 +1,54 @@ | |
+<?xml version="1.0" encoding="UTF-8"?> | |
+<!-- | |
+ ~ 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. | |
+ --> | |
+ | |
+<project xmlns="http://maven.apache.org/POM/4.0.0" | |
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
+ <parent> | |
+ <artifactId>commons-proxy2-parent</artifactId> | |
+ <groupId>org.apache.commons</groupId> | |
+ <version>2.0-SNAPSHOT</version> | |
+ </parent> | |
+ <modelVersion>4.0.0</modelVersion> | |
+ | |
+ <artifactId>commons-proxy2-asm4</artifactId> | |
+ <name>Commons Proxy ASM4 Proxies Module</name> | |
+ <dependencies> | |
+ <dependency> | |
+ <groupId>${project.groupId}</groupId> | |
+ <artifactId>commons-proxy2</artifactId> | |
+ <version>${project.version}</version> | |
+ </dependency> | |
+ <dependency> | |
+ <groupId>org.apache.xbean</groupId> | |
+ <artifactId>xbean-asm4-shaded</artifactId> | |
+ </dependency> | |
+ <dependency> | |
+ <groupId>${project.groupId}</groupId> | |
+ <artifactId>commons-proxy2</artifactId> | |
+ <version>${project.version}</version> | |
+ <type>test-jar</type> | |
+ <scope>test</scope> | |
+ </dependency> | |
+ <dependency> | |
+ <groupId>junit</groupId> | |
+ <artifactId>junit</artifactId> | |
+ <scope>test</scope> | |
+ </dependency> | |
+ </dependencies> | |
+</project> | |
Index: stub/pom.xml | |
=================================================================== | |
--- stub/pom.xml (revision 1509084) | |
+++ stub/pom.xml (working copy) | |
@@ -59,5 +59,11 @@ | |
<version>${project.version}</version> | |
<scope>test</scope> | |
</dependency> | |
+ <dependency> | |
+ <groupId>${project.groupId}</groupId> | |
+ <artifactId>commons-proxy2-asm4</artifactId> | |
+ <version>${project.version}</version> | |
+ <scope>test</scope> | |
+ </dependency> | |
</dependencies> | |
</project> | |
Index: stub/src/test/java/org/apache/commons/proxy2/stub/ASM4StubProxyFactoryTest.java | |
=================================================================== | |
--- stub/src/test/java/org/apache/commons/proxy2/stub/ASM4StubProxyFactoryTest.java (working copy) | |
+++ stub/src/test/java/org/apache/commons/proxy2/stub/ASM4StubProxyFactoryTest.java (working copy) | |
@@ -18,17 +18,11 @@ | |
package org.apache.commons.proxy2.stub; | |
import org.apache.commons.proxy2.ProxyFactory; | |
-import org.apache.commons.proxy2.javassist.JavassistProxyFactory; | |
+import org.apache.commons.proxy2.asm4.ASM4ProxyFactory; | |
-/** | |
- * Test {@link StubProxyFactory} with {@link JavassistProxyFactory}. | |
- */ | |
-public class JavassistStubProxyFactoryTest extends AbstractStubProxyFactoryTest { | |
- /** | |
- * {@inheritDoc} | |
- */ | |
+public class ASM4StubProxyFactoryTest extends AbstractStubProxyFactoryTest { | |
@Override | |
protected ProxyFactory createParent() { | |
- return new JavassistProxyFactory(); | |
+ return new ASM4ProxyFactory(); | |
} | |
} | |
Index: pom.xml | |
=================================================================== | |
--- pom.xml (revision 1509084) | |
+++ pom.xml (working copy) | |
@@ -23,6 +23,7 @@ | |
<modules> | |
<module>core</module> | |
<module>jdk</module> | |
+ <module>asm4</module> | |
<module>javassist</module> | |
<module>cglib</module> | |
<module>stub</module> | |
@@ -210,6 +211,11 @@ | |
<version>3.0</version> | |
</dependency> | |
<dependency> | |
+ <groupId>org.apache.xbean</groupId> | |
+ <artifactId>xbean-asm4-shaded</artifactId> | |
+ <version>3.14</version> | |
+ </dependency> | |
+ <dependency> | |
<groupId>jmock</groupId> | |
<artifactId>jmock</artifactId> | |
<version>1.0.1</version> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment