Java tutorial
/* * Fabric3 * Copyright (c) 2009-2013 Metaform Systems * * Fabric3 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version, with the * following exception: * * Linking this software statically or dynamically with other * modules is making a combined work based on this software. * Thus, the terms and conditions of the GNU General Public * License cover the whole combination. * * As a special exception, the copyright holders of this software * give you permission to link this software with independent * modules to produce an executable, regardless of the license * terms of these independent modules, and to copy and distribute * the resulting executable under terms of your choice, provided * that you also meet, for each linked independent module, the * terms and conditions of the license of that module. An * independent module is a module which is not derived from or * based on this software. If you modify this software, you may * extend this exception to your version of the software, but * you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. * * Fabric3 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the * GNU General Public License along with Fabric3. * If not, see <http://www.gnu.org/licenses/>. */ package org.fabric3.monitor.impl.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import org.fabric3.api.annotation.monitor.MonitorLevel; import org.fabric3.api.host.monitor.MonitorCreationException; import org.fabric3.api.host.monitor.MonitorProxyServiceExtension; import org.fabric3.api.host.monitor.Monitorable; import org.fabric3.monitor.spi.event.MonitorEventEntry; import org.fabric3.monitor.spi.event.ParameterEntry; import org.fabric3.monitor.impl.router.RingBufferDestinationRouter; import org.fabric3.spi.classloader.BytecodeClassLoader; import org.fabric3.spi.monitor.DispatchInfo; import org.oasisopen.sca.annotation.Reference; import org.oasisopen.sca.annotation.Service; 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 static org.fabric3.api.host.monitor.DestinationRouter.DEFAULT_DESTINATION; /** * Performs bytecode generation at runtime to create a monitor proxy. * <p/> * The monitor proxy avoids object creation such as auto-boxing and varargs for highly performant environments. This is done by dynamically generating * writeParameters method with a specific number of arguments for each proxy interface method. The implementation of the proxy interface method invokes this * writeParameters method. Performance characteristics should therefore be the same as hand-implementing the proxy interface. * <p/> * As a further optimization, the {@link DispatchInfo} for an invoked proxy method will be looked up in an array based on the method index. This will be * noticeably faster than looking up the DispatchInfo in a Map keyed by Method as required by JDK proxies. * <p/> * The implementation creates code similar to the following: * <p/> * <pre> * <code> * public void invoke([Type]arg1, [Type]arg2...) throws Throwable { * int index = 1; * MonitorLevel currentLevel; * String currentMessage; * if (level != null) { * currentLevel = level; * currentMessage = template; * } else { * DispatchInfo info = infos[index]; * currentLevel = info.getLevel(); * currentMessage = info.getMessage(); * } * if (currentLevel == null || currentLevel.intValue() < monitorable.getLevel().intValue()) { * // monitoring is off * return; * } * long timestamp = System.currentTimeMillis(); * if (asyncEnabled) { * MonitorEventEntry entry = null; * try { * long start = System.nanoTime(); * entry = router.get(); * entry.setDestinationIndex(destinationIndex); * entry.setTimestampNanos(start); * entry.setTemplate(currentMessage); * entry.setTimestamp(System.currentTimeMillis); * * writeParameters(arg1, arg2,[...other arguments], entry); * } finally { * if (entry != null) { * router.publish(entry); * } * } * } else { * Object[] args = new Object[..]; * args[0] = arg1; * args[1] = arg2; * // ... load other arguments in the array * router.send(currentLevel, destinationIndex, runtimeName, timestamp, source, currentMessage, args); * } * * } * * private int writeParameters(String template, [Type]arg1, [Type]arg2, ..., MonitorEventEntry entry) { * * buffer.limit(numberArgs); * * entry.getParameterEntries[0].setXXXX(arg1); // set argument 1 * .... // set the other arguments * * } * </code> * </pre> */ @Service(MonitorProxyServiceExtension.class) public class BytecodeMonitorProxyService extends AbstractMonitorProxyService implements Opcodes { public static final String PARAM_ENTRY = Type.getInternalName(ParameterEntry.class); public static final String MONITOR_EVENT_ENTRY = Type.getInternalName(MonitorEventEntry.class); public static final String ABSTRACT_MONITOR_HANDLER = Type.getInternalName(AbstractMonitorHandler.class); public static final String DISPATCH_INFO = Type.getInternalName(DispatchInfo.class); public static final String MONITOR_LEVEL = Type.getInternalName(MonitorLevel.class); public static final String DESTINATION_ROUTER = Type.getInternalName(RingBufferDestinationRouter.class); public BytecodeMonitorProxyService(@Reference RingBufferDestinationRouter router, @Reference Monitorable monitorable) { super(router, monitorable); } public <T> T createMonitor(Class<T> type, Monitorable monitorable, String destination) throws MonitorCreationException { if (destination == null) { destination = DEFAULT_DESTINATION; } String proxyClassName = type.getName().replace("$", "") + "_Proxy"; int destinationIndex = router.getDestinationIndex(destination); ClassLoader loader = type.getClassLoader(); Map<Method, DispatchInfo> levels = new LinkedHashMap<Method, DispatchInfo>(); Method[] methods = type.getMethods(); for (Method method : methods) { DispatchInfo info = createDispatchInfo(type, loader, method); levels.put(method, info); } byte[] classBytes = generateClass(type, ClassWriter.COMPUTE_FRAMES); BytecodeClassLoader bytecodeClassLoader = getClassLoader(type); Class<?> clazz = bytecodeClassLoader.defineClass(proxyClassName, classBytes); try { Collection<DispatchInfo> values = levels.values(); DispatchInfo[] infos = values.toArray(new DispatchInfo[values.size()]); AbstractMonitorHandler handler = (AbstractMonitorHandler) clazz.getConstructor().newInstance(); handler.init(destinationIndex, monitorable, router, infos, enabled); return type.cast(handler); } catch (InvocationTargetException e) { throw new MonitorCreationException(e); } catch (NoSuchMethodException e) { throw new MonitorCreationException(e); } catch (InstantiationException e) { throw new MonitorCreationException(e); } catch (IllegalAccessException e) { throw new MonitorCreationException(e); } } /** * Performs the actual bytecode generation to implement the given interface * * @param type the interface to implement * @param flags the ClassWriter flags * @return the generated bytecode * @throws MonitorCreationException if there is an error generating the class */ <T> byte[] generateClass(Class<T> type, int flags) throws MonitorCreationException { String proxyClassNameInternal = Type.getInternalName(type).replace("$", "") + "_Proxy"; String proxyClassDescriptor = "L" + proxyClassNameInternal + ";"; String interfazeName = Type.getInternalName(type); ClassWriter cw = new ClassWriter(flags); cw.visit(Opcodes.V1_7, ACC_PUBLIC + ACC_SUPER, proxyClassNameInternal, null, ABSTRACT_MONITOR_HANDLER, new String[] { interfazeName }); cw.visitSource(type.getName() + "Proxy.java", null); if (type.isLocalClass()) { String enclosingName = Type.getInternalName(type.getEnclosingClass()); cw.visitInnerClass(interfazeName, enclosingName, type.getSimpleName(), ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT + ACC_INTERFACE); } writeConstructor(cw, proxyClassDescriptor); Method[] methods = type.getMethods(); int index = 0; for (Method method : methods) { String signature = calculateWriteParametersSignature(method); Class<?>[] parameterTypes = method.getParameterTypes(); generateMethod(cw, method, index, proxyClassNameInternal, signature); writeGenerateParametersMethod(cw, index, signature, parameterTypes); index++; } cw.visitEnd(); return cw.toByteArray(); } /** * Implements a monitor interface method. * * @param cw the class writer * @param index the method index * @param proxyClassNameInternal the parameter signature * @param writeParametersSignature the parameter types */ private void generateMethod(ClassWriter cw, Method method, int index, String proxyClassNameInternal, String writeParametersSignature) { String methodSignature = Type.getMethodDescriptor(method); // calculate the position of local variables. Per the JVM spec, pos 0 is reserved for a reference to "this" Class<?>[] paramTypes = method.getParameterTypes(); int numParams = paramTypes.length; int offset = calculateParameterSpace(paramTypes); // calculate position of local variables int varIndexPosition = offset + 1; // pos of the index variable used for looking up the DispatchInfo int varCurrentLevelPosition = varIndexPosition + 1; int varCurrentMessagePosition = varCurrentLevelPosition + 1; int varTimestampPosition = varCurrentMessagePosition + 1; int varDispatchInfoPosition = varCurrentMessagePosition + 1; // Note this is the same as varTimestampPos since there is an if int varEntryPosition = varTimestampPosition + 2; // Note +2 int varArgsPosition = varTimestampPosition + 2; // Note +2 and the same as varEntryPosition since there is an if int varStartPosition = varEntryPosition + 1; int varBufferPosition = varStartPosition + 2; MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), methodSignature, null, null); mv.visitCode(); Label l0 = new Label(); Label l1 = new Label(); Label l2 = new Label(); mv.visitTryCatchBlock(l0, l1, l2, null); Label l3 = new Label(); mv.visitTryCatchBlock(l2, l3, l2, null); Label l4 = new Label(); mv.visitLabel(l4); mv.visitLineNumber(62, l4); // set the index var used to lookup the DispatchInfo. The array of DispatchInfo objects correspond to the ordering of Methods in the proxy interface. pushInteger(index, mv); mv.visitVarInsn(ISTORE, varIndexPosition); Label l5 = new Label(); mv.visitLabel(l5); mv.visitLineNumber(65, l5); // lookup the DispatchInfo based on the index for the method mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "infos", "[L" + DISPATCH_INFO + ";"); mv.visitVarInsn(ILOAD, varIndexPosition); mv.visitInsn(AALOAD); mv.visitVarInsn(ASTORE, varDispatchInfoPosition); Label l11 = new Label(); mv.visitLabel(l11); mv.visitLineNumber(70, l11); mv.visitVarInsn(ALOAD, varDispatchInfoPosition); mv.visitMethodInsn(INVOKEVIRTUAL, DISPATCH_INFO, "getLevel", "()L" + MONITOR_LEVEL + ";"); mv.visitVarInsn(ASTORE, varCurrentLevelPosition); Label l12 = new Label(); mv.visitLabel(l12); mv.visitLineNumber(71, l12); mv.visitVarInsn(ALOAD, varDispatchInfoPosition); mv.visitMethodInsn(INVOKEVIRTUAL, DISPATCH_INFO, "getMessage", "()Ljava/lang/String;"); mv.visitVarInsn(ASTORE, varCurrentMessagePosition); mv.visitVarInsn(ALOAD, varCurrentLevelPosition); Label l13 = new Label(); mv.visitJumpInsn(IFNULL, l13); mv.visitVarInsn(ALOAD, varCurrentLevelPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_LEVEL, "intValue", "()I"); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "monitorable", "Lorg/fabric3/api/host/monitor/Monitorable;"); mv.visitMethodInsn(INVOKEINTERFACE, "org/fabric3/api/host/monitor/Monitorable", "getLevel", "()L" + MONITOR_LEVEL + ";"); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_LEVEL, "intValue", "()I"); Label l14 = new Label(); mv.visitJumpInsn(IF_ICMPGE, l14); mv.visitLabel(l13); mv.visitLineNumber(75, l13); mv.visitInsn(RETURN); mv.visitLabel(l14); mv.visitLineNumber(77, l14); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitVarInsn(LSTORE, varTimestampPosition); Label l15 = new Label(); mv.visitLabel(l15); mv.visitLineNumber(78, l15); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "asyncEnabled", "Z"); Label l16 = new Label(); mv.visitJumpInsn(IFEQ, l16); Label l17 = new Label(); mv.visitLabel(l17); mv.visitLineNumber(79, l17); mv.visitInsn(ACONST_NULL); mv.visitVarInsn(ASTORE, varArgsPosition); mv.visitLabel(l0); mv.visitLineNumber(81, l0); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J"); mv.visitVarInsn(LSTORE, varStartPosition); Label l18 = new Label(); mv.visitLabel(l18); mv.visitLineNumber(82, l18); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "router", "L" + DESTINATION_ROUTER + ";"); mv.visitMethodInsn(INVOKEINTERFACE, DESTINATION_ROUTER, "get", "()L" + MONITOR_EVENT_ENTRY + ";"); mv.visitVarInsn(ASTORE, varEntryPosition); Label l19 = new Label(); mv.visitLabel(l19); mv.visitLineNumber(83, l19); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "destinationIndex", "I"); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setDestinationIndex", "(I)V"); Label l20 = new Label(); mv.visitLabel(l20); mv.visitLineNumber(84, l20); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitVarInsn(LLOAD, varStartPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setTimestampNanos", "(J)V"); Label l21 = new Label(); mv.visitLabel(l21); mv.visitLineNumber(85, l21); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "getBuffer", "()Lorg/fabric3/monitor/spi/buffer/ResizableByteBuffer;"); mv.visitVarInsn(ASTORE, varBufferPosition); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitVarInsn(ALOAD, varCurrentMessagePosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setTemplate", "(Ljava/lang/String;)V"); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J"); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setEntryTimestamp", "(J)V"); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitVarInsn(ALOAD, varCurrentLevelPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setLevel", "(L" + MONITOR_LEVEL + ";)V"); Label l22 = new Label(); mv.visitLabel(l22); mv.visitLineNumber(87, l22); mv.visitVarInsn(ALOAD, 0); // Load the method arguments onto the stack. Note that we access the method arguments using i+1 since the 0 position is used by "this" (params begin // at 1). for (int i = 0; i < paramTypes.length; i++) { Class<?> paramType = paramTypes[i]; if (paramType.isPrimitive()) { if (Integer.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, i + 1); } else if (Long.TYPE.equals(paramType)) { mv.visitVarInsn(LLOAD, i + 1); } else if (Double.TYPE.equals(paramType)) { mv.visitVarInsn(DLOAD, i + 1); } else if (Boolean.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, i + 1); } else if (Float.TYPE.equals(paramType)) { mv.visitVarInsn(FLOAD, i + 1); } else if (Short.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, i + 1); } else if (Byte.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, i + 1); } else if (Character.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, i + 1); } else { throw new AssertionError("Unhandled type: " + paramType); } } else { mv.visitVarInsn(ALOAD, i + 1); } } mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitMethodInsn(INVOKESPECIAL, proxyClassNameInternal, "writeParameters" + index, writeParametersSignature); Label l24 = new Label(); mv.visitLabel(l24); mv.visitLineNumber(90, l24); mv.visitLabel(l1); mv.visitLineNumber(95, l1); mv.visitVarInsn(ALOAD, varEntryPosition); Label l27 = new Label(); mv.visitJumpInsn(IFNULL, l27); Label l28 = new Label(); mv.visitLabel(l28); mv.visitLineNumber(96, l28); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "router", "L" + DESTINATION_ROUTER + ";"); mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitMethodInsn(INVOKEINTERFACE, DESTINATION_ROUTER, "publish", "(L" + MONITOR_EVENT_ENTRY + ";)V"); mv.visitJumpInsn(GOTO, l27); mv.visitLabel(l2); mv.visitLineNumber(95, l2); mv.visitVarInsn(ASTORE, 13); mv.visitLabel(l3); mv.visitVarInsn(ALOAD, varEntryPosition); Label l29 = new Label(); mv.visitJumpInsn(IFNULL, l29); Label l30 = new Label(); mv.visitLabel(l30); mv.visitLineNumber(96, l30); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "router", "L" + DESTINATION_ROUTER + ";"); mv.visitVarInsn(ALOAD, varArgsPosition); mv.visitMethodInsn(INVOKEINTERFACE, DESTINATION_ROUTER, "publish", "(L" + MONITOR_EVENT_ENTRY + ";)V"); mv.visitLabel(l29); mv.visitVarInsn(ALOAD, 13); mv.visitInsn(ATHROW); mv.visitLabel(l27); mv.visitLineNumber(99, l27); Label l31 = new Label(); mv.visitJumpInsn(GOTO, l31); mv.visitLabel(l16); mv.visitLineNumber(100, l16); pushInteger(numParams, mv); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(ASTORE, varArgsPosition); Label l32 = new Label(); mv.visitLabel(l32); mv.visitLineNumber(101, l32); for (int i = 0; i < paramTypes.length; i++) { mv.visitVarInsn(ALOAD, varArgsPosition); pushInteger(i, mv); if (paramTypes[i].isPrimitive()) { // i+1 since that is the position of the method argument (position 0 is reserved for "this") if (Integer.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(ILOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } else if (Long.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(LLOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); } else if (Double.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(DLOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); } else if (Float.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(FLOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); } else if (Boolean.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(ILOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Z)Ljava/lang/Boolean;"); } else if (Short.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(ILOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); } else if (Byte.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(ILOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); } else if (Character.TYPE.equals(paramTypes[i])) { mv.visitVarInsn(ILOAD, i + 1); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); } } else { mv.visitVarInsn(ALOAD, i + 1); // i+1 since that is the position of the method argument (position 0 is reserved for "this") } mv.visitInsn(AASTORE); } Label l34 = new Label(); mv.visitLabel(l34); mv.visitLineNumber(103, l34); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "router", "L" + DESTINATION_ROUTER + ";"); mv.visitVarInsn(ALOAD, varCurrentLevelPosition); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "destinationIndex", "I"); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "runtimeName", "Ljava/lang/String;"); mv.visitVarInsn(LLOAD, varTimestampPosition); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, ABSTRACT_MONITOR_HANDLER, "source", "Ljava/lang/String;"); mv.visitVarInsn(ALOAD, varCurrentMessagePosition); mv.visitVarInsn(ALOAD, varArgsPosition); mv.visitMethodInsn(INVOKEINTERFACE, DESTINATION_ROUTER, "send", "(Lorg/fabric3/api/annotation/monitor/MonitorLevel;ILjava/lang/String;JLjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V"); mv.visitLabel(l31); mv.visitLineNumber(106, l31); mv.visitInsn(RETURN); Label methodEnd = new Label(); mv.visitLabel(methodEnd); mv.visitLocalVariable("this", "Lorg/fabric3/monitor/impl/proxy/AbstractMonitorHandler;", null, l4, methodEnd, 0); // Load the method params as local variables. Note the index starts at 1 since 0 is reserved for "this". for (int i = 1; i <= numParams; i++) { Class<?> paramType = paramTypes[i - 1]; if (String.class.equals(paramType)) { mv.visitLocalVariable("arg" + i, "Ljava/lang/String;", null, l4, methodEnd, i); } else if (Integer.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "I", null, l4, methodEnd, i); } else if (Long.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "J", null, l4, methodEnd, i); } else if (Double.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "D", null, l4, methodEnd, i); } else if (Boolean.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "Z", null, l4, methodEnd, i); } else if (Float.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "F", null, l4, methodEnd, i); } else if (Short.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "S", null, l4, methodEnd, i); } else if (Byte.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "B", null, l4, methodEnd, i); } else if (Character.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "C", null, l4, methodEnd, i); } else if (paramType.isPrimitive()) { throw new AssertionError("Unhandled type: " + paramType); } else { mv.visitLocalVariable("arg" + i, "Ljava/lang/Object;", null, l4, methodEnd, i); } } mv.visitLocalVariable("index", "I", null, l5, methodEnd, varIndexPosition); mv.visitLocalVariable("currentLevel", "L" + MONITOR_LEVEL + ";", null, l12, methodEnd, varCurrentLevelPosition); mv.visitLocalVariable("currentMessage", "Ljava/lang/String;", null, l12, methodEnd, varCurrentMessagePosition); mv.visitLocalVariable("timestamp", "J", null, l15, methodEnd, varTimestampPosition); mv.visitLocalVariable("info", "L" + DISPATCH_INFO + ";", null, l11, l12, varDispatchInfoPosition); mv.visitLocalVariable("entry", "L" + MONITOR_EVENT_ENTRY + ";", null, l0, l27, varEntryPosition); mv.visitLocalVariable("args", "[Ljava/lang/Object;", null, l32, l31, varArgsPosition); mv.visitLocalVariable("start", "J", null, l18, l1, varStartPosition); mv.visitLocalVariable("buffer", "Lorg/fabric3/monitor/spi/buffer/ResizableByteBuffer;", null, l22, l1, varBufferPosition); mv.visitMaxs(9, 14); mv.visitEnd(); } /** * Creates the writeParameters method. The method signature will take the same arguments as the proxy interface method that it is to be invoked from. * * @param cw the class writer * @param index the method index * @param signature the parameter signature * @param paramTypes the parameter types */ private void writeGenerateParametersMethod(ClassWriter cw, int index, String signature, Class<?>[] paramTypes) { int varMethodArgOffset = 1; int offset = calculateParameterSpace(paramTypes); int varEntryPosition = offset + 1; int varNumberArgsPosition = varEntryPosition + 1; int varParamEntryPosition = varNumberArgsPosition + 1; int varIPosition = varParamEntryPosition + 1; MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "writeParameters" + index, signature, null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(103, l0); // set the number of arguments for this method pushInteger(paramTypes.length, mv); mv.visitVarInsn(ISTORE, varNumberArgsPosition); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLineNumber(104, l1); mv.visitVarInsn(ALOAD, varEntryPosition); //set the param entry limit mv.visitVarInsn(ILOAD, varNumberArgsPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "setLimit", "(I)V"); Label l2 = new Label(); mv.visitLabel(l2); mv.visitLineNumber(112, l2); // Setup i variable for the for loop and then iterate until the number of arguments is reached mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, varIPosition); Label l3 = new Label(); mv.visitLabel(l3); // jump if i (specified by the for loop) is greater than the number of arguments mv.visitVarInsn(ILOAD, varIPosition); mv.visitVarInsn(ILOAD, varNumberArgsPosition); Label l4 = new Label(); mv.visitJumpInsn(IF_ICMPGE, l4); Label endIf = new Label(); for (int i = 0; i < paramTypes.length; i++) { // load ring buffer entry mv.visitVarInsn(ALOAD, varEntryPosition); mv.visitMethodInsn(INVOKEVIRTUAL, MONITOR_EVENT_ENTRY, "getEntries", "()[L" + PARAM_ENTRY + ";"); pushInteger(i, mv); mv.visitInsn(AALOAD); mv.visitVarInsn(ASTORE, varParamEntryPosition); mv.visitVarInsn(ALOAD, varParamEntryPosition); Class<?> paramType = paramTypes[i]; if (Character.TYPE.equals(paramType)) { // load method parameter mv.visitVarInsn(ILOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setCharValue", "(C)V"); } else if (Integer.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setIntValue", "(I)V"); } else if (Long.TYPE.equals(paramType)) { mv.visitVarInsn(LLOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setLongValue", "(J)V"); } else if (Double.TYPE.equals(paramType)) { mv.visitVarInsn(DLOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setDoubleValue", "(D)V"); } else if (Boolean.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setBooleanValue", "(Z)V"); } else if (Float.TYPE.equals(paramType)) { mv.visitVarInsn(FLOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setFloatValue", "(F)V"); } else if (Short.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setShortValue", "(S)V"); } else if (Byte.TYPE.equals(paramType)) { mv.visitVarInsn(ILOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setByteValue", "(B)V"); } else if (Object.class.isAssignableFrom(paramType)) { mv.visitVarInsn(ALOAD, varMethodArgOffset + i); mv.visitMethodInsn(INVOKEVIRTUAL, PARAM_ENTRY, "setObjectValue", "(Ljava/lang/Object;)V"); } else { throw new AssertionError("Unhandled type: " + paramType); } } mv.visitLabel(endIf); mv.visitLineNumber(121, endIf); // increment i and counter, then loop mv.visitIincInsn(varIPosition, 1); mv.visitJumpInsn(GOTO, l3); // end of for loop mv.visitLabel(l4); mv.visitInsn(RETURN); Label endMethod = new Label(); mv.visitLabel(endMethod); mv.visitLocalVariable("this", "L" + ABSTRACT_MONITOR_HANDLER + ";", null, l0, endMethod, 0); for (int i = 1; i <= paramTypes.length; i++) { Class<?> paramType = paramTypes[i - 1]; if (Integer.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "I", null, l0, endMethod, i + 1); } else if (Long.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "J", null, l0, endMethod, i + 1); } else if (Double.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "D", null, l0, endMethod, i + 1); } else if (Boolean.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "Z", null, l0, endMethod, i + 1); } else if (Float.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "F", null, l0, endMethod, i + 1); } else if (Short.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "S", null, l0, endMethod, i + 1); } else if (Byte.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "B", null, l0, endMethod, i + 1); } else if (Character.TYPE.equals(paramType)) { mv.visitLocalVariable("arg" + i, "C", null, l0, endMethod, i + 1); } else if (paramType.isPrimitive()) { throw new AssertionError("Unhandled type"); } else { mv.visitLocalVariable("arg" + i, "Ljava/lang/Object;", null, l0, endMethod, i + 1); } } mv.visitLocalVariable("entry", "L" + MONITOR_EVENT_ENTRY + ";", null, l0, endMethod, varEntryPosition); mv.visitLocalVariable("numberArgs", "I", null, l1, endMethod, varNumberArgsPosition); mv.visitLocalVariable("current", "L" + PARAM_ENTRY + ";", null, l2, endMethod, varParamEntryPosition); mv.visitLocalVariable("i", "I", null, l2, endMethod, varIPosition); mv.visitMaxs(0, 0); mv.visitEnd(); } /** * Creates a writeParameters method based on the number of arguments for the proxy interface method in the form: * <pre> * void writeParameters(String template, <type> arg1, <type> arg2, ...<type> argN, MonitorEventEntry entry) * </pre> * The method sets the parameters on the MonitorEventEntry instance. * * @param method the proxy interface method * @return the signature */ private String calculateWriteParametersSignature(Method method) { Class<?>[] paramTypes = method.getParameterTypes(); StringBuilder paramSignature = new StringBuilder("("); for (Class<?> paramType : paramTypes) { paramSignature.append(Type.getDescriptor(paramType)); } paramSignature.append("L").append(MONITOR_EVENT_ENTRY).append(";)V"); return paramSignature.toString(); } private void writeConstructor(ClassWriter cw, String proxyClassDescriptor) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(56, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, ABSTRACT_MONITOR_HANDLER, "<init>", "()V"); mv.visitInsn(RETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", proxyClassDescriptor, null, l0, l1, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } /** * Pushes an integer onto the stack * * @param value the value to push * @param mv the method visitor */ private void pushInteger(int value, MethodVisitor mv) { if (value == 0) { mv.visitInsn(ICONST_0); } else if (value == 1) { mv.visitInsn(ICONST_1); } else if (value == 2) { mv.visitInsn(ICONST_2); } else if (value == 3) { mv.visitInsn(ICONST_3); } else if (value == 4) { mv.visitInsn(ICONST_4); } else if (value == 5) { mv.visitInsn(ICONST_5); } else { mv.visitIntInsn(BIPUSH, value); } } /** * Calculates the stack space needed by the given parameters. Doubles and Longs occupy two slots; other types occupy one spot. * * @param paramTypes the parameter types * @return the stack space */ private int calculateParameterSpace(Class<?>[] paramTypes) { int offset = 0; for (Class<?> paramType : paramTypes) { if (Double.TYPE.equals(paramType) || Long.TYPE.equals(paramType)) { offset = offset + 2; } else { offset++; } } return offset; } /** * Returns a classloader for loading the proxy class, creating one if necessary. * * @param type the type to generate a classloader for * @return the classloader */ private BytecodeClassLoader getClassLoader(Class<?> type) { ClassLoader parent = type.getClassLoader(); ClassLoader extensionClassLoader = getClass().getClassLoader(); BytecodeClassLoader classLoader = new BytecodeClassLoader(URI.create("BytecodeClassLoader"), parent); classLoader.addParent(extensionClassLoader); // proxy classes need to be visible as well return classLoader; } }