Created
July 28, 2013 15:57
-
-
Save rmannibucau/6099063 to your computer and use it in GitHub Desktop.
commons-proxy asm impl
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: src/test/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactoryTest.java | |
=================================================================== | |
--- src/test/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactoryTest.java (revision 0) | |
+++ src/test/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactoryTest.java (working copy) | |
@@ -0,0 +1,110 @@ | |
+/* | |
+ * 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.proxy.factory.asm4; | |
+ | |
+import org.apache.commons.proxy.Interceptor; | |
+import org.apache.commons.proxy.Invocation; | |
+import org.apache.commons.proxy.Invoker; | |
+import org.apache.commons.proxy.ObjectProvider; | |
+import org.apache.commons.proxy.ProxyFactory; | |
+import org.apache.commons.proxy.exception.ProxyFactoryException; | |
+import org.junit.Test; | |
+ | |
+import java.lang.reflect.Method; | |
+import java.util.concurrent.atomic.AtomicReference; | |
+ | |
+import static org.junit.Assert.assertEquals; | |
+import static org.junit.Assert.assertTrue; | |
+ | |
+public class ASMProxyFactoryTest { | |
+ @Test | |
+ public void delegate() { | |
+ final Foo delegate = new Foo() { | |
+ @Override | |
+ public String name() { | |
+ return "delegate"; | |
+ } | |
+ }; | |
+ | |
+ final ProxyFactory factory = new ASMProxyFactory(); | |
+ assertTrue(factory.canProxy(new Class<?>[]{ Foo.class })); | |
+ | |
+ final Foo proxy = Foo.class.cast(factory.createDelegatorProxy(getClass().getClassLoader(), new ObjectProvider() { | |
+ @Override | |
+ public Object getObject() { | |
+ return delegate; | |
+ } | |
+ }, new Class<?>[]{ Foo.class })); | |
+ | |
+ assertEquals("delegate", proxy.name()); | |
+ | |
+ delegate.setName("foo"); | |
+ assertEquals("delegate", proxy.name()); | |
+ } | |
+ | |
+ @Test | |
+ public void intercept() { | |
+ final AtomicReference<String> result = new AtomicReference<String>(); | |
+ final Interceptor interceptor = new Interceptor() { | |
+ @Override | |
+ public Object intercept(Invocation invocation) throws Throwable { | |
+ result.set(String.class.cast(invocation.proceed())); | |
+ return result.get(); | |
+ } | |
+ }; | |
+ | |
+ final ProxyFactory factory = new ASMProxyFactory(); | |
+ assertTrue(factory.canProxy(new Class<?>[]{ Foo.class })); | |
+ | |
+ final Foo proxy = Foo.class.cast(factory.createInterceptorProxy(getClass().getClassLoader(), new Foo(), interceptor, new Class<?>[]{Foo.class})); | |
+ proxy.name(); | |
+ assertEquals("foo", result.get()); | |
+ } | |
+ | |
+ @Test | |
+ public void invoke() { | |
+ final Invoker invoker = new Invoker() { | |
+ @Override | |
+ public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { | |
+ return "invoker"; | |
+ } | |
+ }; | |
+ | |
+ final ProxyFactory factory = new ASMProxyFactory(); | |
+ assertTrue(factory.canProxy(new Class<?>[]{ Foo.class })); | |
+ | |
+ final Foo proxy = Foo.class.cast(factory.createInvokerProxy(getClass().getClassLoader(), invoker, new Class<?>[]{Foo.class})); | |
+ assertEquals("invoker", proxy.name()); | |
+ } | |
+ | |
+ public static class Foo { | |
+ private String name = "foo"; | |
+ | |
+ public String name() { | |
+ return "foo"; | |
+ } | |
+ | |
+ public void setName(final String name) { | |
+ this.name = name; | |
+ } | |
+ | |
+ public String exception() throws ProxyFactoryException { | |
+ return "ex"; | |
+ } | |
+ } | |
+} | |
+ | |
Index: src/main/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactory.java | |
=================================================================== | |
--- src/main/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactory.java (revision 0) | |
+++ src/main/java/org/apache/commons/proxy/factory/asm4/ASMProxyFactory.java (working copy) | |
@@ -0,0 +1,917 @@ | |
+/* | |
+ * 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.proxy.factory.asm4; | |
+ | |
+import org.apache.commons.proxy.Interceptor; | |
+import org.apache.commons.proxy.Invocation; | |
+import org.apache.commons.proxy.Invoker; | |
+import org.apache.commons.proxy.ObjectProvider; | |
+import org.apache.commons.proxy.ProxyFactory; | |
+import org.apache.commons.proxy.ProxyUtils; | |
+import org.apache.commons.proxy.exception.ProxyFactoryException; | |
+import org.apache.commons.proxy.factory.util.ProxyClassGenerator; | |
+ | |
+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.ArrayList; | |
+import java.util.Arrays; | |
+import java.util.Collection; | |
+import java.util.HashMap; | |
+import java.util.List; | |
+import java.util.Map; | |
+import java.util.concurrent.locks.ReentrantLock; | |
+ | |
+public class ASMProxyFactory extends ProxyFactory { | |
+ @Override | |
+ public boolean canProxy(final Class[] proxyClasses) { | |
+ try { | |
+ findImpl(proxyClasses); | |
+ return true; | |
+ } catch (final ProxyFactoryException ex) { | |
+ return false; | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Object createDelegatorProxy(final ClassLoader classLoader, final ObjectProvider targetProvider, | |
+ final Class[] proxyClasses) { | |
+ try { | |
+ return proxy(classLoader, proxyClasses, new DelegatorInvocationHandler(targetProvider)); | |
+ } catch (final ProxyFactoryException pfe) { | |
+ return super.createDelegatorProxy(classLoader, targetProvider, proxyClasses); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Object createInterceptorProxy(final ClassLoader classLoader, final Object target, final Interceptor interceptor, | |
+ final Class[] proxyClasses) { | |
+ try { | |
+ return proxy(classLoader, proxyClasses, new InterceptorInvocationHandler(target, interceptor)); | |
+ } catch (final ProxyFactoryException pfe) { | |
+ return super.createInterceptorProxy(classLoader, target, interceptor, proxyClasses); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Object createInvokerProxy(final ClassLoader classLoader, final Invoker invoker, | |
+ final Class[] proxyClasses) { | |
+ try { | |
+ return proxy(classLoader, proxyClasses, new InvokerInvocationHandler(invoker)); | |
+ } catch (final ProxyFactoryException pfe) { | |
+ pfe.printStackTrace(); | |
+ return super.createInvokerProxy(classLoader, invoker, proxyClasses); | |
+ } | |
+ } | |
+ | |
+ public static Object proxy(final ClassLoader classLoader, final Class[] proxyClasses, final InvocationHandler handler) { | |
+ final Class<?> impl = findImpl(proxyClasses); | |
+ if (impl == null) { | |
+ throw new ProxyFactoryException("no class to subclass"); | |
+ } | |
+ | |
+ final Collection<Class> interfaces = new ArrayList<Class>(); | |
+ interfaces.addAll(Arrays.asList(proxyClasses)); | |
+ interfaces.remove(impl); | |
+ return ProxyGenerator.newProxyInstance(classLoader, handler, impl, interfaces.toArray(new Class[interfaces.size()])); | |
+ } | |
+ | |
+ private static Class<?> findImpl(final Class<?>[] proxyClasses) { | |
+ Class<?> impl = null; | |
+ for (final Class<?> c : proxyClasses) { | |
+ if (!c.isInterface()) { | |
+ if (impl != null) { | |
+ throw new ProxyFactoryException("Can't proxy multiple parent"); | |
+ } | |
+ impl = c; | |
+ } | |
+ } | |
+ return impl; | |
+ } | |
+ | |
+ private static class ProxyGenerator implements ProxyClassGenerator { | |
+ private static final String HANDLER_NAME = "__handler"; | |
+ private static final ReentrantLock LOCK = new ReentrantLock(); | |
+ | |
+ public static Object newProxyInstance(final ClassLoader classLoader, final InvocationHandler handler, final Class classToSubclass, final Class... interfaces) throws ProxyFactoryException { | |
+ try { | |
+ final Class proxyClass = createProxy(classToSubclass, classLoader, interfaces); | |
+ return constructProxy(proxyClass, handler); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ } | |
+ | |
+ public static Object 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 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 cl, final String proxyName, final Class... interfaces) { | |
+ final String classFileName = proxyName.replace('.', '/'); | |
+ | |
+ try { | |
+ return cl.loadClass(proxyName); | |
+ } catch (Exception e) { | |
+ // no-op | |
+ } | |
+ | |
+ LOCK.lock(); | |
+ try { | |
+ | |
+ try { // Try it again, another thread may have beaten this one... | |
+ return cl.loadClass(proxyName); | |
+ } catch (Exception e) { | |
+ // no-op | |
+ } | |
+ | |
+ final byte[] proxyBytes = generateProxy(classToProxy, classFileName, interfaces); | |
+ return Unsafe.defineClass(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 Class... interfaces) { | |
+ return createProxy(classToProxy, cl, classToProxy.getName() + "$$CommonsProxy", interfaces); | |
+ } | |
+ | |
+ public static byte[] generateProxy(final Class<?> classToProxy, final String proxyName, final Class<?>... interfaces) throws ProxyFactoryException { | |
+ final ASMFacade.Writer cw; | |
+ try { | |
+ cw = ASMFacade.newClassWriter(); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ | |
+ 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(ASMFacade.V1_5, ASMFacade.ACC_PUBLIC + ASMFacade.ACC_SUPER, proxyClassFileName, null, classFileName, interfaceNames); | |
+ cw.visitSource(classFileName + ".java", null); | |
+ | |
+ // push InvocationHandler fields | |
+ cw.visitField(ASMFacade.ACC_FINAL + ASMFacade.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); | |
+ } | |
+ | |
+ // Iterate over the public methods | |
+ for (final Map.Entry<String, List<Method>> entry : methodMap.entrySet()) { | |
+ | |
+ for (final Method method : entry.getValue()) { | |
+ 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 ASMFacade.Writer 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 = ASMFacade.ACC_PUBLIC; | |
+ } else if (Modifier.isProtected(modifiers)) { | |
+ modifier = ASMFacade.ACC_PROTECTED; | |
+ } | |
+ | |
+ final ASMFacade.PassThrough 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 ASMFacade.Label l0 = ASMFacade.newLabel(); | |
+ final ASMFacade.Label l1 = ASMFacade.newLabel(); | |
+ final ASMFacade.Label l2 = ASMFacade.newLabel(); | |
+ | |
+ 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(ASMFacade.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(ASMFacade.DUP); | |
+ | |
+ final Class<?> parameterType = parameterTypes[i]; | |
+ | |
+ // push number onto stack | |
+ pushIntOntoStack(mv, i); | |
+ | |
+ if (parameterType.isPrimitive()) { | |
+ final String wrapperType = getWrapperType(parameterType); | |
+ mv.visitFieldInsn(ASMFacade.GETSTATIC, wrapperType, "TYPE", "Ljava/lang/Class;"); | |
+ } else { | |
+ mv.visitLdcInsn(ASMFacade.getType(getAsmTypeAsString(parameterType, true))); | |
+ } | |
+ | |
+ mv.visitInsn(ASMFacade.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(ASMFacade.INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); | |
+ | |
+ // store the returned method for later | |
+ mv.visitVarInsn(ASMFacade.ASTORE, length); | |
+ | |
+ // the following code generates bytecode equivalent to: | |
+ // return ((<returntype>) invocationHandler.invoke(this, method, new Object[] { <function arguments }))[.<primitive>Value()]; | |
+ | |
+ final ASMFacade.Label l4 = ASMFacade.newLabel(); | |
+ mv.visitLabel(l4); | |
+ mv.visitVarInsn(ASMFacade.ALOAD, 0); | |
+ | |
+ // get the invocationHandler field from this class | |
+ mv.visitFieldInsn(ASMFacade.GETFIELD, proxyName, handlerName, "Ljava/lang/reflect/InvocationHandler;"); | |
+ | |
+ // we want to pass "this" in as the first parameter | |
+ mv.visitVarInsn(ASMFacade.ALOAD, 0); | |
+ | |
+ // and the method we fetched earlier | |
+ mv.visitVarInsn(ASMFacade.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(ASMFacade.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(ASMFacade.INVOKESTATIC, wrapperType, "valueOf", "(" + getPrimitiveLetter(parameterType) + ")L" + wrapperType + ";"); | |
+ mv.visitInsn(ASMFacade.AASTORE); | |
+ | |
+ if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) { | |
+ index += 2; | |
+ } else { | |
+ index++; | |
+ } | |
+ } else { | |
+ mv.visitVarInsn(ASMFacade.ALOAD, index); | |
+ mv.visitInsn(ASMFacade.AASTORE); | |
+ index++; | |
+ } | |
+ } | |
+ | |
+ // invoke the invocationHandler | |
+ mv.visitMethodInsn(ASMFacade.INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); | |
+ | |
+ // cast the result | |
+ mv.visitTypeInsn(ASMFacade.CHECKCAST, getCastType(returnType)); | |
+ | |
+ if (returnType.isPrimitive() && (!Void.TYPE.equals(returnType))) { | |
+ // get the primitive value | |
+ mv.visitMethodInsn(ASMFacade.INVOKEVIRTUAL, getWrapperType(returnType), getPrimitiveMethod(returnType), "()" + getPrimitiveLetter(returnType)); | |
+ } | |
+ | |
+ // push return | |
+ mv.visitLabel(l1); | |
+ if (!Void.TYPE.equals(returnType)) { | |
+ mv.visitInsn(getReturnInsn(returnType)); | |
+ } else { | |
+ mv.visitInsn(ASMFacade.POP); | |
+ mv.visitInsn(ASMFacade.RETURN); | |
+ } | |
+ | |
+ // catch InvocationTargetException | |
+ if (exceptionTypes.length > 0) { | |
+ mv.visitLabel(l2); | |
+ mv.visitVarInsn(ASMFacade.ASTORE, length); | |
+ | |
+ final ASMFacade.Label l5 = ASMFacade.newLabel(); | |
+ mv.visitLabel(l5); | |
+ | |
+ for (int i = 0; i < exceptionTypes.length; i++) { | |
+ final Class<?> exceptionType = exceptionTypes[i]; | |
+ | |
+ mv.visitLdcInsn(ASMFacade.getType("L" + exceptionType.getName().replace('.', '/') + ";")); | |
+ mv.visitVarInsn(ASMFacade.ALOAD, length); | |
+ mv.visitMethodInsn(ASMFacade.INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;"); | |
+ mv.visitMethodInsn(ASMFacade.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); | |
+ mv.visitMethodInsn(ASMFacade.INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z"); | |
+ | |
+ final ASMFacade.Label l6 = ASMFacade.newLabel(); | |
+ mv.visitJumpInsn(ASMFacade.IFEQ, l6); | |
+ | |
+ final ASMFacade.Label l7 = ASMFacade.newLabel(); | |
+ mv.visitLabel(l7); | |
+ | |
+ mv.visitVarInsn(ASMFacade.ALOAD, length); | |
+ mv.visitMethodInsn(ASMFacade.INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;"); | |
+ mv.visitTypeInsn(ASMFacade.CHECKCAST, exceptionType.getName().replace('.', '/')); | |
+ mv.visitInsn(ASMFacade.ATHROW); | |
+ mv.visitLabel(l6); | |
+ | |
+ if (i == (exceptionTypes.length - 1)) { | |
+ mv.visitTypeInsn(ASMFacade.NEW, "java/lang/reflect/UndeclaredThrowableException"); | |
+ mv.visitInsn(ASMFacade.DUP); | |
+ mv.visitVarInsn(ASMFacade.ALOAD, length); | |
+ mv.visitMethodInsn(ASMFacade.INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"); | |
+ mv.visitInsn(ASMFacade.ATHROW); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // finish this method | |
+ mv.visitMaxs(0, 0); | |
+ mv.visitEnd(); | |
+ } | |
+ | |
+ /** | |
+ * Gets the appropriate bytecode instruction for RETURN, according to what type we need to return | |
+ * | |
+ * @param type Type the needs to be returned | |
+ * @return The matching bytecode instruction | |
+ */ | |
+ private static int getReturnInsn(final Class<?> type) { | |
+ if (type.isPrimitive()) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return ASMFacade.IRETURN; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return ASMFacade.IRETURN; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return ASMFacade.IRETURN; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return ASMFacade.IRETURN; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return ASMFacade.IRETURN; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return ASMFacade.FRETURN; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return ASMFacade.LRETURN; | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return ASMFacade.DRETURN; | |
+ } | |
+ } | |
+ | |
+ return ASMFacade.ARETURN; | |
+ } | |
+ | |
+ /** | |
+ * Returns the appropriate bytecode instruction to load a value from a variable to the stack | |
+ * | |
+ * @param type Type to load | |
+ * @return Bytecode instruction to use | |
+ */ | |
+ private static int getVarInsn(final Class<?> type) { | |
+ if (type.isPrimitive()) { | |
+ if (Integer.TYPE.equals(type)) { | |
+ return ASMFacade.ILOAD; | |
+ } else if (Boolean.TYPE.equals(type)) { | |
+ return ASMFacade.ILOAD; | |
+ } else if (Character.TYPE.equals(type)) { | |
+ return ASMFacade.ILOAD; | |
+ } else if (Byte.TYPE.equals(type)) { | |
+ return ASMFacade.ILOAD; | |
+ } else if (Short.TYPE.equals(type)) { | |
+ return ASMFacade.ILOAD; | |
+ } else if (Float.TYPE.equals(type)) { | |
+ return ASMFacade.FLOAD; | |
+ } else if (Long.TYPE.equals(type)) { | |
+ return ASMFacade.LLOAD; | |
+ } else if (Double.TYPE.equals(type)) { | |
+ return ASMFacade.DLOAD; | |
+ } | |
+ } | |
+ | |
+ throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type"); | |
+ } | |
+ | |
+ /** | |
+ * Returns the name of the Java method to call to get the primitive value from an Object - e.g. intValue for java.lang.Integer | |
+ * | |
+ * @param type Type whose primitive method we want to lookup | |
+ * @return The name of the method to use | |
+ */ | |
+ 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"); | |
+ } | |
+ | |
+ /** | |
+ * Gets the string to use for CHECKCAST instruction, returning the correct value for any type, including primitives and arrays | |
+ * | |
+ * @param returnType The type to cast to with CHECKCAST | |
+ * @return CHECKCAST parameter | |
+ */ | |
+ static String getCastType(final Class<?> returnType) { | |
+ if (returnType.isPrimitive()) { | |
+ return getWrapperType(returnType); | |
+ } else { | |
+ return getAsmTypeAsString(returnType, false); | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Returns the wrapper type for a primitive, e.g. java.lang.Integer for int | |
+ */ | |
+ 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"); | |
+ } | |
+ | |
+ /** | |
+ * Invokes the most appropriate bytecode instruction to put a number on the stack | |
+ */ | |
+ private static void pushIntOntoStack(final ASMFacade.PassThrough mv, final int i) { | |
+ if (i == 0) { | |
+ mv.visitInsn(ASMFacade.ICONST_0); | |
+ } else if (i == 1) { | |
+ mv.visitInsn(ASMFacade.ICONST_1); | |
+ } else if (i == 2) { | |
+ mv.visitInsn(ASMFacade.ICONST_2); | |
+ } else if (i == 3) { | |
+ mv.visitInsn(ASMFacade.ICONST_3); | |
+ } else if (i == 4) { | |
+ mv.visitInsn(ASMFacade.ICONST_4); | |
+ } else if (i == 5) { | |
+ mv.visitInsn(ASMFacade.ICONST_5); | |
+ } else if (i > 5 && i <= 255) { | |
+ mv.visitIntInsn(ASMFacade.BIPUSH, i); | |
+ } else { | |
+ mv.visitIntInsn(ASMFacade.SIPUSH, i); | |
+ } | |
+ } | |
+ | |
+ private static void createArrayDefinition(final ASMFacade.PassThrough 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(ASMFacade.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"); | |
+ } | |
+ | |
+ /** | |
+ * Converts a class to a String suitable for ASM. | |
+ * | |
+ * @param parameterType Class to convert | |
+ * @param wrap True if a non-array object should be wrapped with L and ; - e.g. Ljava/lang/Integer; | |
+ * @return String to use for ASM | |
+ */ | |
+ 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('.', '/'); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * The methods of this class model sun.misc.Unsafe which is used reflectively | |
+ */ | |
+ 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 Class<?> clsToProxy, final String proxyName, final byte[] proxyBytes) throws IllegalAccessException, InvocationTargetException { | |
+ return (Class<?>) defineClass.invoke(unsafe, proxyName, proxyBytes, 0, proxyBytes.length, clsToProxy.getClassLoader(), clsToProxy.getProtectionDomain()); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Class generateProxyClass(final ClassLoader classLoader, final Class[] proxyClasses) { | |
+ return createProxy(proxyClasses[0], classLoader); | |
+ } | |
+ } | |
+ | |
+ //////////////// 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, 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 target; | |
+ | |
+ public ReflectionInvocation(Object target, Method method, Object[] arguments) { | |
+ this.method = method; | |
+ this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments); | |
+ this.target = target; | |
+ } | |
+ | |
+ public Object[] getArguments() { | |
+ return arguments; | |
+ } | |
+ | |
+ public Method getMethod() { | |
+ return method; | |
+ } | |
+ | |
+ public Object getProxy() { | |
+ return target; | |
+ } | |
+ | |
+ public Object proceed() throws Throwable { | |
+ try { | |
+ return method.invoke(target, arguments); | |
+ } catch (InvocationTargetException e) { | |
+ throw e.getTargetException(); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
Index: src/main/java/org/apache/commons/proxy/factory/asm4/ASMFacade.java | |
=================================================================== | |
--- src/main/java/org/apache/commons/proxy/factory/asm4/ASMFacade.java (revision 0) | |
+++ src/main/java/org/apache/commons/proxy/factory/asm4/ASMFacade.java (working copy) | |
@@ -0,0 +1,280 @@ | |
+/* | |
+ * 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.proxy.factory.asm4; | |
+ | |
+import org.apache.commons.proxy.exception.ProxyFactoryException; | |
+ | |
+import java.lang.reflect.Field; | |
+import java.lang.reflect.InvocationHandler; | |
+import java.lang.reflect.Method; | |
+import java.lang.reflect.Modifier; | |
+import java.lang.reflect.Proxy; | |
+ | |
+public class ASMFacade { | |
+ public static int V1_5 = 49; | |
+ public static int DUP = 89; | |
+ public static int ACC_PUBLIC = 0x0001; | |
+ public static int ACC_PRIVATE = 0x0002; | |
+ public static int ACC_PROTECTED = 0x0004; | |
+ public static int ACC_SUPER = 0x0020; | |
+ public static int ACC_FINAL = 0x0010; | |
+ public static int GETSTATIC = 178; | |
+ public static int AASTORE = 83; | |
+ public static int INVOKEVIRTUAL = 182; | |
+ public static int ASTORE = 58; | |
+ public static int ALOAD = 25; | |
+ public static int GETFIELD = 180; | |
+ public static int INVOKESTATIC = 184; | |
+ public static int INVOKEINTERFACE = 185; | |
+ public static int CHECKCAST = 192; | |
+ public static int POP = 87; | |
+ public static int RETURN = 177; | |
+ public static int IFEQ = 153; | |
+ public static int ATHROW = 191; | |
+ public static int NEW = 187; | |
+ public static int INVOKESPECIAL = 183; | |
+ public static int IRETURN = 172; // visitInsn | |
+ public static int LRETURN = 173; | |
+ public static int FRETURN = 174; | |
+ public static int DRETURN = 175; | |
+ public static int ARETURN = 176; | |
+ public static int ILOAD = 21; | |
+ public static int LLOAD = 22; | |
+ public static int FLOAD = 23; | |
+ public static int DLOAD = 24; | |
+ public static int ICONST_0 = 3; | |
+ public static int ICONST_1 = 4; | |
+ public static int ICONST_2 = 5; | |
+ public static int ICONST_3 = 6; | |
+ public static int ICONST_4 = 7; | |
+ public static int ICONST_5 = 8; | |
+ public static int BIPUSH = 16; | |
+ public static int SIPUSH = 17; | |
+ public static int ANEWARRAY = 189; | |
+ | |
+ private static Class<?> cwClass; | |
+ private static Class<?> typeClass; | |
+ private static Class<?> labelClass; | |
+ | |
+ public static Writer newClassWriter() { | |
+ try { | |
+ final Object classWriter = getASMClass().getConstructor(int.class).newInstance(1); // COMPUTE_MAXS; | |
+ return Writer.class.cast( | |
+ Proxy.newProxyInstance(ASMFacade.class.getClassLoader(), | |
+ new Class<?>[]{Writer.class}, | |
+ new ReflectionHandler(classWriter) | |
+ )); | |
+ | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ } | |
+ | |
+ public static Type getType(final String c) { | |
+ try { | |
+ return Type.class.cast( | |
+ Proxy.newProxyInstance(ASMFacade.class.getClassLoader(), | |
+ new Class<?>[]{Type.class}, | |
+ new ReflectionHandler(typeClass.getMethod("getType", String.class).invoke(null, c)))); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ } | |
+ | |
+ public static Label newLabel() { | |
+ try { | |
+ return Label.class.cast( | |
+ Proxy.newProxyInstance(ASMFacade.class.getClassLoader(), | |
+ new Class<?>[]{Label.class}, | |
+ new ReflectionHandler(labelClass.newInstance()) | |
+ )); | |
+ } catch (final Exception e) { | |
+ throw new ProxyFactoryException(e); | |
+ } | |
+ } | |
+ | |
+ public static interface Writer { | |
+ void visit(int version, int access, | |
+ String name, String signature, String superName, | |
+ String[] interfaces); | |
+ | |
+ void visitSource(String file, String debug); | |
+ | |
+ PassThrough visitField(int access, String name, | |
+ String desc, String signature, Object value); | |
+ | |
+ byte[] toByteArray(); | |
+ | |
+ PassThrough visitMethod(int access, String name, | |
+ String desc, String signature, String[] exceptions); | |
+ } | |
+ | |
+ public static interface Label { | |
+ | |
+ } | |
+ | |
+ public static interface Type { | |
+ | |
+ } | |
+ | |
+ public static interface PassThrough { | |
+ void visitEnd(); | |
+ | |
+ void visitCode(); | |
+ | |
+ void visitTryCatchBlock(Label l0, Label l1, Label l2, String s); | |
+ | |
+ void visitLabel(Label l0); | |
+ | |
+ void visitLdcInsn(String name); | |
+ | |
+ void visitInsn(int dup); | |
+ | |
+ void visitFieldInsn(int getstatic, String wrapperType, String type, String s); | |
+ | |
+ void visitMethodInsn(int invokevirtual, String s, String getDeclaredMethod, String s1); | |
+ | |
+ void visitVarInsn(int astore, int length); | |
+ | |
+ void visitTypeInsn(int checkcast, String castType); | |
+ | |
+ void visitLdcInsn(Type type); | |
+ | |
+ void visitJumpInsn(int ifeq, Label l6); | |
+ | |
+ void visitMaxs(int i, int i1); | |
+ | |
+ void visitIntInsn(int bipush, int i); | |
+ } | |
+ | |
+ private static void tryClass(final String s) { | |
+ if (cwClass == null) { | |
+ try { | |
+ cwClass = ASMFacade.class.getClassLoader().loadClass(s); | |
+ labelClass = ASMFacade.class.getClassLoader().loadClass(getASMClass().getPackage().getName() + ".Label"); | |
+ typeClass = ASMFacade.class.getClassLoader().loadClass(getASMClass().getPackage().getName() + ".Type"); | |
+ | |
+ final Class<?> opCodesClass = ASMFacade.class.getClassLoader().loadClass(getASMClass().getPackage().getName() + ".OpCodes"); | |
+ for (final Field f : ASMFacade.class.getDeclaredFields()) { | |
+ if (Modifier.isStatic(f.getModifiers())) { | |
+ f.setAccessible(true); | |
+ f.set(null, opCodesClass.getDeclaredField(f.getName())); | |
+ } | |
+ } | |
+ } catch (final Throwable t) { | |
+ //ignore | |
+ } | |
+ } | |
+ } | |
+ | |
+ private static synchronized Class<?> getASMClass() throws ProxyFactoryException { | |
+ if (cwClass == null) { | |
+ //try the "real" asm first, then the others | |
+ tryClass("org.objectweb.asm.ClassWriter"); | |
+ tryClass("org.apache.xbean.asm4.ClassWriter"); | |
+ tryClass("org.apache.xbean.asm.ClassWriter"); | |
+ tryClass("org.springframework.asm.ClassWriter"); | |
+ if (cwClass == null) { | |
+ throw new ProxyFactoryException("Can't find asm"); | |
+ } | |
+ } | |
+ return cwClass; | |
+ } | |
+ | |
+ private static class ReflectionHandler implements InvocationHandler { | |
+ private final Object delegate; | |
+ private final Class<?> delegateClass; | |
+ | |
+ public ReflectionHandler(final Object classWriter) { | |
+ delegate = classWriter; | |
+ delegateClass = delegate.getClass(); | |
+ } | |
+ | |
+ @Override | |
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { | |
+ if (Object.class.equals(method.getDeclaringClass())) { | |
+ return method.invoke(delegate, args); | |
+ } | |
+ | |
+ Method delegateMethod = null; | |
+ try { | |
+ delegateMethod = delegateClass.getMethod(method.getName(), convertParameters(method.getParameterTypes())); | |
+ } catch (final NoSuchMethodException nsme) { | |
+ for (final Method m : delegateClass.getMethods()) { | |
+ if (m.getName().equals(method.getName())) { | |
+ delegateMethod = m; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (delegateMethod == null) { | |
+ throw new ProxyFactoryException("method " + method + " not found"); | |
+ } | |
+ | |
+ delegateMethod.setAccessible(true); // needed since some class are package protected | |
+ | |
+ final Object result = delegateMethod.invoke(delegate, convertArgs(args)); | |
+ if (result != null && result.getClass().getName().startsWith(cwClass.getPackage().getName())) { | |
+ return Proxy.newProxyInstance(ASMFacade.class.getClassLoader(), | |
+ new Class<?>[]{PassThrough.class}, | |
+ new ReflectionHandler(result)); | |
+ } | |
+ return result; | |
+ } | |
+ | |
+ private static Class<?>[] convertParameters(final Class<?>[] parameterTypes) { | |
+ if (parameterTypes == null) { | |
+ return null; | |
+ } | |
+ | |
+ final Class<?>[] types = new Class<?>[parameterTypes.length]; | |
+ for (int i = 0; i < types.length; i++) { | |
+ if (Label.class.equals(parameterTypes[i])) { | |
+ types[i] = labelClass; | |
+ } else if (Type.class.equals(parameterTypes[i])) { | |
+ types[i] = typeClass; | |
+ } else { | |
+ types[i] = parameterTypes[i]; | |
+ } | |
+ } | |
+ return types; | |
+ } | |
+ | |
+ private static Object[] convertArgs(final Object[] args) { | |
+ if (args == null) { | |
+ return null; | |
+ } | |
+ | |
+ final Object[] converted = new Object[args.length]; | |
+ for (int i = 0; i < args.length; i++) { | |
+ if (args[i] == null) { | |
+ converted[i] = null; | |
+ } else if (Proxy.isProxyClass(args[i].getClass())) { | |
+ final InvocationHandler handler = Proxy.getInvocationHandler(args[i]); | |
+ if (ReflectionHandler.class.isInstance(handler)) { | |
+ converted[i] = ReflectionHandler.class.cast(handler).delegate; | |
+ } else { | |
+ converted[i] = args[i]; | |
+ } | |
+ } else { | |
+ converted[i] = args[i]; | |
+ } | |
+ } | |
+ return converted; | |
+ } | |
+ } | |
+} | |
Index: pom.xml | |
=================================================================== | |
--- pom.xml (revision 1507798) | |
+++ pom.xml (working copy) | |
@@ -200,6 +200,18 @@ | |
<version>2.3</version> | |
<scope>test</scope> | |
</dependency> | |
+ <dependency> | |
+ <groupId>junit</groupId> | |
+ <artifactId>junit</artifactId> | |
+ <version>4.11</version> | |
+ <scope>test</scope> | |
+ </dependency> | |
+ <dependency> | |
+ <groupId>org.apache.xbean</groupId> | |
+ <artifactId>xbean-asm4-shaded</artifactId> | |
+ <version>3.14</version> | |
+ <scope>test</scope> | |
+ </dependency> | |
</dependencies> | |
<reporting> | |
@@ -244,8 +256,8 @@ | |
</distributionManagement> | |
<properties> | |
- <maven.compile.source>1.4</maven.compile.source> | |
- <maven.compile.target>1.4</maven.compile.target> | |
+ <maven.compile.source>1.6</maven.compile.source> | |
+ <maven.compile.target>1.6</maven.compile.target> | |
<commons.componentid>proxy</commons.componentid> | |
<commons.release.version>1.0</commons.release.version> | |
<commons.binary.suffix></commons.binary.suffix> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment