package org.jctools.channels.proxy; import java.io.PrintWriter; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.jctools.channels.WaitStrategy; import org.jctools.channels.mpsc.MpscOffHeapFixedSizeRingBuffer; import org.jctools.channels.spsc.SpscOffHeapFixedSizeRingBuffer; import org.jctools.util.UnsafeAccess; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.util.TraceClassVisitor; import sun.misc.Unsafe; public class ProxyChannelFactory { /** * The index of the 'this' object in instance methods */ private static final int LOCALS_INDEX_THIS = 0; private static final boolean DEBUG = Boolean.getBoolean("jctools.debug"); private static void printClassBytes(byte[] byteCode) { if (DEBUG) { TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out)); new ClassReader(byteCode).accept(visitor, 0); } } public static long writeAcquireWithWaitStrategy(ProxyChannelRingBuffer channelBackend, WaitStrategy waitStrategy) { long wOffset; int idleCounter = 0; while ((wOffset = channelBackend.writeAcquire()) == ProxyChannelRingBuffer.EOF) { idleCounter = waitStrategy.idle(idleCounter); } return wOffset; } /** * Create a default single producer single consumer (SPSC) proxy channel. * * @param capacity * The minimum capacity for unprocessed invocations the channel * should support * @param iFace * Interface the proxy must implement * @param waitStrategy * A wait strategy to be invoked when the backing data structure * is full * @return A proxy channel instance */ public static <E> ProxyChannel<E> createSpscProxy(int capacity, Class<E> iFace, WaitStrategy waitStrategy) { return createProxy(capacity, iFace, waitStrategy, SpscOffHeapFixedSizeRingBuffer.class); } /** * Create a default multi producer single consumer (MPSC) proxy channel. * * @param capacity * The minimum capacity for unprocessed invocations the channel * should support * @param iFace * Interface the proxy must implement * @param waitStrategy * A wait strategy to be invoked when the backing data structure * is full * @return A proxy channel instance */ public static <E> ProxyChannel<E> createMpscProxy(int capacity, Class<E> iFace, WaitStrategy waitStrategy) { return createProxy(capacity, iFace, waitStrategy, MpscOffHeapFixedSizeRingBuffer.class); } /** * Create a proxy channel using a user supplied back end. * * @param capacity * The minimum capacity for unprocessed invocations the channel * should support * @param iFace * Interface the proxy must implement * @param waitStrategy * A wait strategy to be invoked when the backing data structure * is full * @param backendType * The back end type, the proxy will inherit from this channel * type. The back end type must define a constructor with signature: * <code>(int capacity, int primitiveMessageSize, int referenceMessageSize)</code> * @return A proxy channel instance */ public static <E> ProxyChannel<E> createProxy(int capacity, Class<E> iFace, WaitStrategy waitStrategy, Class<? extends ProxyChannelRingBuffer> backendType) { if (!iFace.isInterface()) { throw new IllegalArgumentException("Not an interface: " + iFace); } String generatedName = Type.getInternalName(iFace) + "$JCTools$ProxyChannel$" + backendType.getSimpleName(); Class<?> preExisting = findExisting(generatedName, iFace); if (preExisting != null) { return instantiate(preExisting, capacity, waitStrategy); } List<Method> relevantMethods = findRelevantMethods(iFace); if (relevantMethods.isEmpty()) { throw new IllegalArgumentException("Does not declare any abstract methods: " + iFace); } // max number of reference arguments of any method int referenceMessageSize = 0; // max bytes required to store the primitive args of a call frame of any method int primitiveMessageSize = 0; for (Method method : relevantMethods) { int primitiveMethodSize = 0; int referenceCount = 0; for (Class<?> parameterType : method.getParameterTypes()) { if (parameterType.isPrimitive()) { primitiveMethodSize += primitiveMemorySize(parameterType); } else { referenceCount++; } } primitiveMessageSize = Math.max(primitiveMessageSize, primitiveMethodSize); referenceMessageSize = Math.max(referenceMessageSize, referenceCount); } // We need to add an int to this for the 'type' value on the message frame primitiveMessageSize += primitiveMemorySize(int.class); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); classWriter.visit(Opcodes.V1_4, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, generatedName, null, Type.getInternalName(backendType), new String[]{Type.getInternalName(ProxyChannel.class), Type.getInternalName(iFace)}); implementInstanceFields(classWriter); implementConstructor(classWriter, backendType, generatedName, primitiveMessageSize, referenceMessageSize); implementProxyInstance(classWriter, iFace, generatedName); implementProxy(classWriter, iFace, generatedName); implementUserMethods(classWriter, relevantMethods, generatedName, backendType); implementProcess(classWriter, backendType, relevantMethods, iFace, generatedName); classWriter.visitEnd(); synchronized (ProxyChannelFactory.class) { preExisting = findExisting(generatedName, iFace); if (preExisting != null) { return instantiate(preExisting, capacity, waitStrategy); } byte[] byteCode = classWriter.toByteArray(); printClassBytes(byteCode); // Caveat: The interface and JCTools must be on the same class loader. Maybe class loader should be an argument? Overload? Class<?> definedClass = UnsafeAccess.UNSAFE.defineClass(generatedName, byteCode, 0, byteCode.length, iFace.getClassLoader(), null); return instantiate(definedClass, capacity, waitStrategy); } } private static void implementUserMethods(ClassWriter classWriter, List<Method> relevantMethods, String generatedName, Class<?extends ProxyChannelRingBuffer> backendType) { int type = 1; for (Method method : relevantMethods) { implementUserMethod(method, classWriter, type++, generatedName, backendType); } } private static List<Method> findRelevantMethods(Class<?> iFace) { Method[] methods = iFace.getMethods(); List<Method> relevantMethods = new ArrayList<Method>(methods.length); for (Method method : methods) { if (Modifier.isAbstract(method.getModifiers())) { relevantMethods.add(method); } } return relevantMethods; } private static Class<?> findExisting(String generatedName, Class<?> iFace) { try { String className = generatedName.replace("/", "."); return Class.forName(className, true, iFace.getClassLoader()); } catch (ClassNotFoundException e) { return null; } } @SuppressWarnings("unchecked") private static <E> ProxyChannel<E> instantiate(Class<?> proxy, int capacity, WaitStrategy waitStrategy) { try { return (ProxyChannel<E>) proxy.getDeclaredConstructor(int.class, WaitStrategy.class).newInstance(capacity, waitStrategy); } catch (Exception e) { throw new IllegalStateException(e); } } private static void implementProcess(ClassVisitor classVisitor, Class<? extends ProxyChannelRingBuffer> backendType, List<Method> methods, Class<?> iFace, String generatedName) { // public int process (E impl, int limit) MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, "process", methodDescriptor(int.class, iFace, int.class), null, null); methodVisitor.visitCode(); LocalsHelper locals = LocalsHelper.forInstanceMethod(); int localIndexOfImpl = locals.newLocal(iFace); int localIndexOfLimit = locals.newLocal(int.class); int localIndexOfLoopIndex = locals.newLocal(int.class); int localIndexOfROffset = locals.newLocal(long.class); //int i = 0; methodVisitor.visitInsn(Opcodes.ICONST_0); methodVisitor.visitVarInsn(Opcodes.ISTORE, localIndexOfLoopIndex); // label <loopStart>; Label loopStart = new Label(), loopEnd = new Label(); methodVisitor.visitLabel(loopStart); // if (i < limit) goto <loopEnd>; methodVisitor.visitVarInsn(Opcodes.ILOAD, localIndexOfLoopIndex); methodVisitor.visitVarInsn(Opcodes.ILOAD, localIndexOfLimit); methodVisitor.visitJumpInsn(Opcodes.IF_ICMPGE, loopEnd); // prepare switch labels Label endOfSwitch = new Label(); Label[] cases = new Label[methods.size()]; for (int index = 0; index < cases.length; index++) { cases[index] = new Label(); } // long rOffset = this.readAcquire(); readAcquire(methodVisitor, backendType); methodVisitor.visitVarInsn(Opcodes.LSTORE, localIndexOfROffset); // if (rOffset == EOF) goto <loopEnd>; methodVisitor.visitVarInsn(Opcodes.LLOAD, localIndexOfROffset); methodVisitor.visitLdcInsn(ProxyChannelRingBuffer.EOF); methodVisitor.visitInsn(Opcodes.LCMP); methodVisitor.visitJumpInsn(Opcodes.IFEQ, loopEnd); // switch(UnsafeAccess.UNSAFE.getInt(rOffset)) // start with case 1, increment by 1; represents "type" getUnsafe(methodVisitor, int.class, localIndexOfROffset, 0); methodVisitor.visitTableSwitchInsn(1, cases.length, endOfSwitch, cases); // add case statement for each method for (int index = 0; index < cases.length; index++) { // case <index>: methodVisitor.visitLabel(cases[index]); Method method = methods.get(index); // #PUSH: impl methodVisitor.visitVarInsn(Opcodes.ALOAD, localIndexOfImpl); int localIndexOfArrayReferenceBaseIndex = Integer.MIN_VALUE; for (Class<?> parameterType : method.getParameterTypes()) { if (!parameterType.isPrimitive()) { // long referenceArrayIndex = this.consumerReferenceArrayIndex(); consumerReferenceArrayIndex(methodVisitor, backendType); localIndexOfArrayReferenceBaseIndex = locals.newLocal(long.class); methodVisitor.visitVarInsn(Opcodes.LSTORE, localIndexOfArrayReferenceBaseIndex); break; } } // # R_OFFSET_DELTA = 4 // #FOREACH param in method int rOffsetDelta = 4; int arrayReferenceBaseIndexDelta = 0; for (Class<?> parameterType : method.getParameterTypes()) { if (parameterType.isPrimitive()) { // #PUSH: UnsafeAccess.UNSAFE.get[param.type](rOffset + #R_OFFSET_DELTA); // #R_OFFSET_DELTA += if param.type in {long, double} 8 else 4; getUnsafe(methodVisitor, parameterType, localIndexOfROffset, rOffsetDelta); rOffsetDelta += primitiveMemorySize(parameterType); } else { getReference(methodVisitor, parameterType, localIndexOfArrayReferenceBaseIndex, arrayReferenceBaseIndexDelta, backendType); arrayReferenceBaseIndexDelta++; } } // #END // this.readRelease(rOffset); readRelease(methodVisitor, localIndexOfROffset, backendType); // method.invoke(impl, <args>); methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(iFace), method.getName(), Type.getMethodDescriptor(method), true); // break; methodVisitor.visitJumpInsn(Opcodes.GOTO, endOfSwitch); } // label <endOfSwitch>; methodVisitor.visitLabel(endOfSwitch); // i++; methodVisitor.visitIincInsn(3, 1); // goto <loopStart>; methodVisitor.visitJumpInsn(Opcodes.GOTO, loopStart); methodVisitor.visitLabel(loopEnd); // return i; methodVisitor.visitVarInsn(Opcodes.ILOAD, localIndexOfLoopIndex); methodVisitor.visitInsn(Opcodes.IRETURN); // size requirement is computed by ASM; complete method. methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); implementBridgeMethod(classVisitor, generatedName, "process", int.class, iFace, int.class); } private static void implementInstanceFields(ClassVisitor classVisitor) { classVisitor.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "waitStrategy", Type.getDescriptor(WaitStrategy.class), null, null); } private static void implementConstructor(ClassVisitor classVisitor, Class<? extends ProxyChannelRingBuffer> parentType, String generatedName, int primitiveMessageSize, int referenceMessageSize) { MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, "<init>", methodDescriptor(void.class, int.class, WaitStrategy.class), null, null); methodVisitor.visitCode(); LocalsHelper locals = LocalsHelper.forInstanceMethod(); int localIndexOfCapacity = locals.newLocal(int.class); int localIndexOfWaitStrategy = locals.newLocal(WaitStrategy.class); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitVarInsn(Opcodes.ILOAD, localIndexOfCapacity); methodVisitor.visitLdcInsn(primitiveMessageSize); methodVisitor.visitLdcInsn(referenceMessageSize); methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentType), "<init>", methodDescriptor(void.class, int.class, int.class, int.class), false); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitVarInsn(Opcodes.ALOAD, localIndexOfWaitStrategy); methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, generatedName, "waitStrategy", Type.getDescriptor(WaitStrategy.class)); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); } private static void implementProxyInstance(ClassVisitor classVisitor, Class<?> iFace, String generatedName) { MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, "proxyInstance", methodDescriptor(iFace, iFace), null, null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitInsn(Opcodes.ARETURN); methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); implementBridgeMethod(classVisitor, generatedName, "proxyInstance", iFace, iFace); } private static void implementProxy(ClassVisitor classVisitor, Class<?> iFace, String generatedName) { MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, "proxy", methodDescriptor(iFace), null, null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitInsn(Opcodes.ARETURN); methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); implementBridgeMethod(classVisitor, generatedName, "proxy", iFace); } private static void implementBridgeMethod(ClassVisitor classVisitor, String generatedName, String methodName, Class<?> returnType, Class<?>... parameterTypes) { Class<?> bridgeMethodReturnType = returnType.isPrimitive() ? returnType : Object.class; Class<?>[] bridgeMethodParameterTypes = new Class<?>[parameterTypes.length]; int parameterIndex = 0; // Bridge methods use only Object's, so replace all non-Object types. for (Class<?> parameterType : parameterTypes) { bridgeMethodParameterTypes[parameterIndex++] = parameterType.isPrimitive() ? parameterType : Object.class; } MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC, methodName, methodDescriptor(bridgeMethodReturnType, bridgeMethodParameterTypes), null, null); methodVisitor.visitCode(); LocalsHelper locals = LocalsHelper.forInstanceMethod(); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); for (Class<?> parameterType : parameterTypes) { int localIndexOfParameter = locals.newLocal(parameterType); int loadOpCode = Type.getType(parameterType).getOpcode(Opcodes.ILOAD); methodVisitor.visitVarInsn(loadOpCode, localIndexOfParameter); if (!parameterType.isPrimitive()) { methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(parameterType)); } } methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedName, methodName, methodDescriptor(returnType, parameterTypes), false); if (!returnType.isPrimitive()) { methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(returnType)); } int returnOpcode = Type.getType(returnType).getOpcode(Opcodes.IRETURN); methodVisitor.visitInsn(returnOpcode); methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); } private static void implementUserMethod(Method method, ClassVisitor classVisitor, int type, String generatedName, Class<? extends ProxyChannelRingBuffer> backendType) { if (method.getReturnType() != void.class) { throw new IllegalArgumentException("Method does not return void: " + method); } String[] exceptions = new String[method.getExceptionTypes().length]; int index = 0; for (Class<?> exceptionType : method.getExceptionTypes()) { exceptions[index++] = Type.getInternalName(exceptionType); } // @Override public void <user interface method> MethodVisitor methodVisitor = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, exceptions.length == 0 ? null : exceptions); methodVisitor.visitCode(); LocalsHelper locals = LocalsHelper.forInstanceMethod(); boolean containsReferences = false; for (Class<?> parameterType : method.getParameterTypes()) { locals.newLocal(parameterType); containsReferences |= !parameterType.isPrimitive(); } int localIndexOfWOffset = locals.newLocal(long.class); // long wOffset = this.writeAcquireWithWaitStrategy(); writeAcquireWithWaitStrategy(methodVisitor, generatedName, backendType); methodVisitor.visitVarInsn(Opcodes.LSTORE, localIndexOfWOffset); int localIndexOfArrayReferenceBaseIndex = Integer.MIN_VALUE; if (containsReferences) { // long arrayReferenceBaseIndex = this.producerReferenceArrayIndex();x producerReferenceArrayIndex(methodVisitor, backendType); localIndexOfArrayReferenceBaseIndex = locals.newLocal(long.class); methodVisitor.visitVarInsn(Opcodes.LSTORE, localIndexOfArrayReferenceBaseIndex); } // #W_OFFSET_DELTA = 4 // #ARGUMENT = 1 // not zero based (zero references "this") // #FOREACH param in method int wOffsetDelta = 4, varOffset = 1; int arrayReferenceBaseIndexDelta = 0; for (Class<?> parameterType : method.getParameterTypes()) { // UnsafeAccess.UNSAFE.put[param.type](wOffset + #W_OFFSET_DELTA, #ARGUMENT); // #W_OFFSET_DELTA += if param.type in {long, double} 8 else 4; /* * wOffset here is being used to identify an index in the local call * frame, do not get it confused with the wOffset local variable * used in examples which is a position in a buffer that we can * write to. wOffsetDelta is the position from the VALUE of the * wOffset local variable to write to. varOffset is the index in the * local variables to read from, i.e. the parameter index in locals */ if (parameterType.isPrimitive()) { varOffset += putUnsafe(methodVisitor, parameterType, localIndexOfWOffset, wOffsetDelta, varOffset); wOffsetDelta += primitiveMemorySize(parameterType); } else { putReference(methodVisitor, parameterType, localIndexOfArrayReferenceBaseIndex, arrayReferenceBaseIndexDelta, varOffset, backendType); varOffset += Type.getType(parameterType).getSize(); arrayReferenceBaseIndexDelta++; } } // #END // this.writeRelease(wOffset, #TYPE); writeRelease(methodVisitor, localIndexOfWOffset, type, backendType); // return; methodVisitor.visitInsn(Opcodes.RETURN); // complete method, ASM computes size requirement. methodVisitor.visitMaxs(-1, -1); methodVisitor.visitEnd(); } private static void producerReferenceArrayIndex(MethodVisitor methodVisitor, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "producerReferenceArrayIndex", "()J", false); } private static void consumerReferenceArrayIndex(MethodVisitor methodVisitor, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "consumerReferenceArrayIndex", "()J", false); } private static void writeAcquireWithWaitStrategy(MethodVisitor methodVisitor, String generatedName, Class<? extends ProxyChannelRingBuffer> backendType) { // One of these is for the getfield bytecode, the other is as the first arg to the writeAcquireWithWaitStrategy methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, generatedName, "waitStrategy", Type.getDescriptor(WaitStrategy.class)); methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(ProxyChannelFactory.class), "writeAcquireWithWaitStrategy", methodDescriptor(long.class, ProxyChannelRingBuffer.class, WaitStrategy.class), false); } private static void writeRelease(MethodVisitor methodVisitor, int wOffset, int type, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitVarInsn(Opcodes.LLOAD, wOffset); methodVisitor.visitLdcInsn(type); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "writeRelease", "(JI)V", false); } private static void readAcquire(MethodVisitor methodVisitor, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "readAcquire", "()J", false); } private static void readRelease(MethodVisitor methodVisitor, int wOffset, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); methodVisitor.visitVarInsn(Opcodes.LLOAD, wOffset); methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "readRelease", "(J)V", false); } private static int getUnsafe(MethodVisitor methodVisitor, Class<?> parameterType, int localIndexOfROffset, int rOffsetDelta) { loadUnsafe(methodVisitor); loadWOffset(methodVisitor, parameterType, localIndexOfROffset, rOffsetDelta); return parameterTypeUnsafe(methodVisitor, parameterType, false); } private static int putUnsafe(MethodVisitor methodVisitor, Class<?> parameterType, int wOffset, int wOffsetDelta, int varOffset) { loadUnsafe(methodVisitor); loadWOffset(methodVisitor, parameterType, wOffset, wOffsetDelta); methodVisitor.visitVarInsn(Type.getType(parameterType).getOpcode(Opcodes.ILOAD), varOffset); return parameterTypeUnsafe(methodVisitor, parameterType, true); } private static void getReference(MethodVisitor methodVisitor, Class<?> parameterType, int localIndexOfArrayReferenceBaseIndex, int arrayReferenceBaseIndexDelta, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); loadLocalIndexAndApplyDelta(methodVisitor, localIndexOfArrayReferenceBaseIndex, arrayReferenceBaseIndexDelta); readReference(methodVisitor, backendType); if (parameterType != Object.class) { methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(parameterType)); } } private static void putReference(MethodVisitor methodVisitor, Class<?> parameterType, int localIndexOfArrayReferenceBaseIndex, int arrayReferenceBaseIndexDelta, int varOffset, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitVarInsn(Opcodes.ALOAD, LOCALS_INDEX_THIS); loadLocalIndexAndApplyDelta(methodVisitor, localIndexOfArrayReferenceBaseIndex, arrayReferenceBaseIndexDelta); methodVisitor.visitVarInsn(Type.getType(parameterType).getOpcode(Opcodes.ILOAD), varOffset); writeReference(methodVisitor, backendType); } private static void loadUnsafe(MethodVisitor methodVisitor) { methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(UnsafeAccess.class), "UNSAFE", "L" + Type.getInternalName(Unsafe.class) + ";"); } private static void loadWOffset(MethodVisitor methodVisitor, Class<?> parameterType, int baseOffset, long wOffsetDelta) { if (parameterType == boolean.class) { methodVisitor.visitInsn(Opcodes.ACONST_NULL); } loadLocalIndexAndApplyDelta(methodVisitor, baseOffset, wOffsetDelta); } private static void loadLocalIndexAndApplyDelta(MethodVisitor methodVisitor, int localVariableIndex, long delta) { methodVisitor.visitVarInsn(Opcodes.LLOAD, localVariableIndex); if (delta != 0) { methodVisitor.visitLdcInsn(delta); methodVisitor.visitInsn(Opcodes.LADD); } } private static int parameterTypeUnsafe(MethodVisitor methodVisitor, Class<?> parameterType, boolean write) { parameterType = parameterType.isPrimitive() ? parameterType : Object.class; Type type = Type.getType(parameterType); String boolDescriptor = parameterType == boolean.class ? "Ljava/lang/Object;" : ""; methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Unsafe.class), (write ? "put" : "get") + Character.toUpperCase(type.getClassName().charAt(0)) + type.getClassName().substring(1), write ? ("(" + boolDescriptor + "J" + type.getDescriptor() + ")V") : ("(" + boolDescriptor + "J)" + type.getDescriptor()), false); return type.getSize(); } private static void writeReference(MethodVisitor methodVisitor, Class<? extends ProxyChannelRingBuffer> backendType) { methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backendType), "writeReference", ("(JLjava/lang/Object;)V"), false); } private static void readReference(MethodVisitor methodVisitor, Class<? extends ProxyChannelRingBuffer> backend) { methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(backend), "readReference", ("(J)Ljava/lang/Object;"), false); } private static int primitiveMemorySize(Class<?> type) { if (!type.isPrimitive()) { throw new IllegalArgumentException("Cannot handle non-primtive parameter type: " + type); } return type == long.class || type == double.class ? 8 : 4; } private static String methodDescriptor(Class<?> returnType, Class<?>... parameterTypes) { Type[] argumentTypes = new Type[parameterTypes.length]; int typeIndex = 0; for (Class<?> parameterType : parameterTypes) { argumentTypes[typeIndex++] = Type.getType(parameterType); } return Type.getMethodDescriptor(Type.getType(returnType), argumentTypes); } }