diff --git a/lib/src/main/java/net/fushizen/invokedynamic/proxy/DelegateFactory.java b/lib/src/main/java/net/fushizen/invokedynamic/proxy/DelegateFactory.java new file mode 100644 index 0000000..c92361c --- /dev/null +++ b/lib/src/main/java/net/fushizen/invokedynamic/proxy/DelegateFactory.java @@ -0,0 +1,52 @@ +package net.fushizen.invokedynamic.proxy; + +import java.lang.invoke.*; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Field; + +public class DelegateFactory { + public static DynamicProxy createDelegatingProxy(Class delegateClass, Class interfaceClass) { + try { + DynamicProxy proxy = DynamicProxy.builder() + .withConstructor(interfaceClass) + .withInterfaces(interfaceClass) + .withSuperclass(delegateClass) + .withInvocationHandler(new DelegateInvocationHandler(delegateClass, interfaceClass)) + .build(); + return proxy; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static class DelegateInvocationHandler implements DynamicInvocationHandler { + private final Class delegateType; + private final Class interfaceType; + + public DelegateInvocationHandler(Class delegateType, Class interfaceType) { + this.delegateType = delegateType; + this.interfaceType = interfaceType; + } + + @Override + public CallSite handleInvocation( + Lookup lookup, + String methodName, + MethodType methodType, + MethodHandle superMethod + ) throws Throwable { + if (superMethod != null) { + return new ConstantCallSite(superMethod.asType(methodType)); + } + + Field field = delegateType.getDeclaredField("delegate"); + field.setAccessible(true); + MethodHandle delegateGetter = lookup.unreflectGetter(field); + + MethodType methodArgs = methodType.dropParameterTypes(0, 1); + MethodHandle interfaceMethod = lookup.findVirtual(interfaceType, methodName, methodArgs); + MethodHandle syntheticMethod = MethodHandles.filterArguments(interfaceMethod, 0, delegateGetter); + return new ConstantCallSite(syntheticMethod.asType(methodType)); + } + } +} diff --git a/lib/src/test/java/net/fushizen/invokedynamic/proxy/DelegateFactoryTest.java b/lib/src/test/java/net/fushizen/invokedynamic/proxy/DelegateFactoryTest.java new file mode 100644 index 0000000..87a9b33 --- /dev/null +++ b/lib/src/test/java/net/fushizen/invokedynamic/proxy/DelegateFactoryTest.java @@ -0,0 +1,42 @@ +package net.fushizen.invokedynamic.proxy; + +import org.junit.Test; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.junit.Assert.assertEquals; + +public class DelegateFactoryTest { + @Test + public void smokeTest() throws Throwable { + DynamicProxy proxy = DelegateFactory.createDelegatingProxy(ReallyBlockingQueue.class, BlockingQueue.class); + LinkedBlockingQueue lbq = new LinkedBlockingQueue<>(); + ReallyBlockingQueue queue = (ReallyBlockingQueue) proxy.constructor().invoke(lbq); + + queue.offer(14); + queue.put(15); + + assertEquals(14, queue.poll()); + assertEquals(15, queue.take()); + } + + public abstract static class ReallyBlockingQueue implements BlockingQueue { + private final BlockingQueue delegate; + + protected ReallyBlockingQueue(BlockingQueue delegate) { + this.delegate = delegate; + } + + @Override + public boolean offer(T t) { + try { + delegate.put(t); + return true; + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return false; + } + } + } +}