List of usage examples for org.objectweb.asm Type getMethodDescriptor
public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes)
From source file:bluejelly.runtime.ModuleLoader.java
License:BSD License
/** * Generate a {@link Caf} subclass for the given method information. * /*from w w w.j a va 2 s. c o m*/ * @param module module declaring the method to process * @param ci code info for the method to process * * @return * a {@link Caf} subclass whose <code>fastEnter</code> will call * module.ci.getMethod() with an {@link ExecutionContext} as argument. */ @SuppressWarnings("unchecked") private Class<Caf> generateCaf(Module module, CodeInfo ci) { String nodeName = this.genNodeName(ci); String cafName = Type.getInternalName(Caf.class); String moduleName = Type.getInternalName(module.getClass()); String methodName = ci.getMethod().getName(); ClassWriter cw = new ClassWriter(0); cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL, nodeName, null, cafName, null); { String consDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.getType(Module.class), Type.getType(String.class) }); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", consDesc, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn(INVOKESPECIAL, cafName, "<init>", consDesc); mv.visitInsn(RETURN); mv.visitMaxs(3, 3); mv.visitEnd(); } this.generateFastEnter(cw, cafName, moduleName, methodName); cw.visitEnd(); byte[] b = cw.toByteArray(); Class<?> clazz = this.loader.defineClass(nodeName, b); return (Class<Caf>) clazz; }
From source file:bluejelly.runtime.ModuleLoader.java
License:BSD License
/** * Generate a {@link Code} subclass for the given method information. * /*from w ww. j a v a 2s. c o m*/ * @param module module declaring the method to process * @param ci code info for the method to process * * @return * a {@link Code} subclass whose <code>fastEnter</code> will call * module.ci.getMethod() with an {@link ExecutionContext} as argument. */ @SuppressWarnings("unchecked") private Class<Code> generateCode(Module module, CodeInfo ci) { String nodeName = this.genNodeName(ci); String codeName = Type.getInternalName(Code.class); String moduleName = Type.getInternalName(module.getClass()); String methodName = ci.getMethod().getName(); ClassWriter cw = new ClassWriter(0); cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL, nodeName, null, codeName, null); { String consDesc = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type.getType(int.class), Type.getType(Module.class), Type.getType(String.class) }); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", consDesc, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ILOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESPECIAL, codeName, "<init>", consDesc); mv.visitInsn(RETURN); mv.visitMaxs(4, 4); mv.visitEnd(); } this.generateFastEnter(cw, codeName, moduleName, methodName); cw.visitEnd(); byte[] b = cw.toByteArray(); Class<?> clazz = this.loader.defineClass(nodeName, b); return (Class<Code>) clazz; }
From source file:ch.eiafr.cojac.instrumenters.FloatProxyMethod.java
License:Apache License
private void convertArgumentsToReal(MethodVisitor mv, ConversionContext cc) { String convertDesc = Type.getMethodDescriptor(OBJ_ARRAY_TYPE, cc.inArgs); createConvertMethod(convertDesc, cc); // stack >> [target] prm0 prm1 prm2... mv.visitMethodInsn(INVOKESTATIC, crtClassName, COJAC_TYPE_CONVERT_NAME, convertDesc, false); // stack >> [target] allParamsArr // We'll need a reference to converted arrays (for merging purposes) // and a copy of the target (trying first without proxy)... if (hasTarget(cc.opcode)) { mv.visitInsn(DUP2);/*from w ww . j a v a2s . com*/ } else { mv.visitInsn(DUP); } // stack >> [target] allParamsArr [target] allParamsArr }
From source file:ch.eiafr.cojac.instrumenters.InvokableMethod.java
License:Apache License
public static String replaceFloatMethodDescription(String desc) { Type[] arguments = Type.getArgumentTypes(desc); Type returnType = Type.getReturnType(desc); Type[] after = new Type[arguments.length]; for (int i = 0; i < arguments.length; i++) after[i] = afterFloatReplacement(arguments[i]); Type returnAfter = afterFloatReplacement(returnType); return Type.getMethodDescriptor(returnAfter, after); }
From source file:cn.annoreg.asm.DelegateGenerator.java
License:Open Source License
public static MethodVisitor generateNonStaticMethod(ClassVisitor parentClass, MethodVisitor parent, String className, String methodName, String desc, final Side side) { //convert desc to a non-static method form String nonstaticDesc;//from ww w. j a v a 2 s . c o m { Type staticType = Type.getMethodType(desc); Type retType = staticType.getReturnType(); Type[] argsType = staticType.getArgumentTypes(); Type[] argsTypeWithThis = new Type[argsType.length + 1]; argsTypeWithThis[0] = Type.getType('L' + className.replace('.', '/') + ';'); System.arraycopy(argsType, 0, argsTypeWithThis, 1, argsType.length); nonstaticDesc = Type.getMethodDescriptor(retType, argsTypeWithThis); } //delegateName is a string used by both sides to identify a network-call delegate. final String delegateName = className + ":" + methodName + ":" + desc; final Type[] args = Type.getArgumentTypes(nonstaticDesc); final Type ret = Type.getReturnType(nonstaticDesc); //Check types for (Type t : args) { //TODO support these types if (!t.getDescriptor().startsWith("L") && !t.getDescriptor().startsWith("[")) { throw new RuntimeException("Unsupported argument type in network call. "); } } if (!ret.equals(Type.VOID_TYPE)) { throw new RuntimeException( "Unsupported return value type in network call. " + "Only void is supported."); } //Generate call to NetworkCallManager in parent. parent.visitCode(); //First parameter parent.visitLdcInsn(delegateName); //Second parameter: object array pushIntegerConst(parent, args.length); //this (0) has been included parent.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); for (int i = 0; i < args.length; ++i) { parent.visitInsn(Opcodes.DUP); pushIntegerConst(parent, i); parent.visitVarInsn(Opcodes.ALOAD, i); parent.visitInsn(Opcodes.AASTORE); } //Call cn.annoreg.mc.network.NetworkCallManager.onNetworkCall parent.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(NetworkCallManager.class), "onNetworkCall", "(Ljava/lang/String;[Ljava/lang/Object;)V"); parent.visitInsn(Opcodes.RETURN); parent.visitMaxs(5, args.length); parent.visitEnd(); //Create delegate object. final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); final String delegateId = Integer.toString(delegateNextID++); final Type delegateClassType = Type.getType("cn/annoreg/asm/NetworkCallDelegate_" + delegateId); cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, delegateClassType.getInternalName(), null, Type.getInternalName(Object.class), new String[] { Type.getInternalName(NetworkCallDelegate.class) }); //package cn.annoreg.asm; //class NetworkCallDelegate_? implements NetworkCallDelegate { { MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } //public NetworkCallDelegate_?() {} final String delegateMethodName = methodName + "_delegate_" + delegateId; { MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "invoke", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object[].class)), null, null); mv.visitCode(); //check if this is null mv.visitVarInsn(Opcodes.ALOAD, 1); //0 is this pushIntegerConst(mv, 0); mv.visitInsn(Opcodes.AALOAD); Label lblEnd = new Label(); mv.visitJumpInsn(Opcodes.IFNULL, lblEnd); for (int i = 0; i < args.length; ++i) { mv.visitVarInsn(Opcodes.ALOAD, 1); //0 is this pushIntegerConst(mv, i); mv.visitInsn(Opcodes.AALOAD); mv.visitTypeInsn(Opcodes.CHECKCAST, args[i].getInternalName()); } mv.visitMethodInsn(Opcodes.INVOKESTATIC, //delegateClassType.getInternalName(), className.replace('.', '/'), delegateMethodName, nonstaticDesc); mv.visitLabel(lblEnd); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(args.length + 2, 2); mv.visitEnd(); } //@Override public void invoke(Object[] args) { // delegated((Type0) args[0], (Type1) args[1], ...); //} //The returned MethodVisitor will visit the original version of the method, //including its annotation, where we can get StorageOptions. return new MethodVisitor(Opcodes.ASM4, parentClass.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, delegateMethodName, nonstaticDesc, null, null)) { //Remember storage options for each argument StorageOption.Option[] options = new StorageOption.Option[args.length]; int targetIndex = -1; double sendRange = -1; StorageOption.Target.RangeOption range = StorageOption.Target.RangeOption.SINGLE; { for (int i = 0; i < options.length; ++i) { options[i] = StorageOption.Option.NULL; //set default value } options[0] = StorageOption.Option.INSTANCE; } @Override public AnnotationVisitor visitParameterAnnotation(int parameter_in_func, String desc, boolean visible) { final int parameter = parameter_in_func + 1; //skip this Type type = Type.getType(desc); if (type.equals(Type.getType(StorageOption.Data.class))) { options[parameter] = StorageOption.Option.DATA; } else if (type.equals(Type.getType(StorageOption.Instance.class))) { //INSTANCE as defualt options[parameter] = StorageOption.Option.INSTANCE; //Change to NULLABLE_INSTANCE if nullable set to true return new AnnotationVisitor(this.api, super.visitParameterAnnotation(parameter, desc, visible)) { @Override public void visit(String name, Object value) { if (name.equals("nullable")) { if ((Boolean) value == true) { options[parameter] = StorageOption.Option.NULLABLE_INSTANCE; } } super.visit(name, value); } }; } else if (type.equals(Type.getType(StorageOption.Update.class))) { options[parameter] = StorageOption.Option.UPDATE; } else if (type.equals(Type.getType(StorageOption.Null.class))) { options[parameter] = StorageOption.Option.NULL; } else if (type.equals(Type.getType(StorageOption.Target.class))) { if (!args[parameter].equals(Type.getType(EntityPlayer.class))) { throw new RuntimeException("Target annotation can only be used on EntityPlayer."); } if (targetIndex != -1) { throw new RuntimeException("You can not specify multiple targets."); } options[parameter] = StorageOption.Option.INSTANCE; targetIndex = parameter; return new AnnotationVisitor(this.api, super.visitParameterAnnotation(parameter, desc, visible)) { @Override public void visitEnum(String name, String desc, String value) { super.visitEnum(name, desc, value); range = StorageOption.Target.RangeOption.valueOf(value); } }; } else if (type.equals(Type.getType(StorageOption.RangedTarget.class))) { if (targetIndex != -1) { throw new RuntimeException("You can not specify multiple targets."); } targetIndex = parameter; range = null; return new AnnotationVisitor(this.api, super.visitParameterAnnotation(parameter, desc, visible)) { @Override public void visit(String name, Object value) { super.visit(name, value); sendRange = (double) value; } }; } return super.visitParameterAnnotation(parameter, desc, visible); } //TODO this option (from annotation) @Override public void visitEnd() { super.visitEnd(); //This is the last method in the delegate class. //Finish the class and do the registration. cw.visitEnd(); try { Class<?> clazz = classLoader.defineClass(delegateClassType.getClassName(), cw.toByteArray()); NetworkCallDelegate delegateObj = (NetworkCallDelegate) clazz.newInstance(); if (side == Side.CLIENT) { NetworkCallManager.registerClientDelegateClass(delegateName, delegateObj, options, targetIndex, range, sendRange); } else { NetworkCallManager.registerServerDelegateClass(delegateName, delegateObj, options); } } catch (Throwable e) { throw new RuntimeException("Can not create delegate for network call.", e); } } }; //public static void delegated(Type0 arg0, Type1, arg1, ...) { // //Code generated by caller. //} //} }
From source file:co.cask.cdap.app.runtime.spark.SparkRunnerClassLoader.java
License:Apache License
/** * Defines the DStreamGraph class by rewriting calls to parallel array with a call to * SparkRuntimeUtils#setTaskSupport(ParArray). *//*from w w w.jav a 2 s . c o m*/ private Class<?> defineDStreamGraph(String name, InputStream byteCodeStream) throws IOException { ClassReader cr = new ClassReader(byteCodeStream); ClassWriter cw = new ClassWriter(0); cr.accept(new ClassVisitor(Opcodes.ASM5, cw) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(Opcodes.ASM5, mv) { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { super.visitMethodInsn(opcode, owner, name, desc, itf); // If detected call to ArrayBuffer.par(), set the TaskSupport to avoid thread leak. //INVOKEVIRTUAL scala/collection/mutable/ ArrayBuffer.par ()Lscala/collection/parallel/mutable/ParArray; Type returnType = Type.getReturnType(desc); if (opcode == Opcodes.INVOKEVIRTUAL && name.equals("par") && owner.equals("scala/collection/mutable/ArrayBuffer") && returnType.getClassName().equals("scala.collection.parallel.mutable.ParArray")) { super.visitMethodInsn(Opcodes.INVOKESTATIC, SPARK_RUNTIME_UTILS_TYPE.getInternalName(), "setTaskSupport", Type.getMethodDescriptor(returnType, returnType), false); } } }; } }, ClassReader.EXPAND_FRAMES); byte[] byteCode = cw.toByteArray(); return defineClass(name, byteCode, 0, byteCode.length); }
From source file:co.paralleluniverse.fibers.instrument.SuspendablesScannerTest.java
License:Open Source License
@Test public void superSuspendableCallTest() { final String method = A.class.getName() + ".foo" + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(IA.class)); assertTrue(suspependables.contains(method)); }
From source file:co.paralleluniverse.fibers.instrument.SuspendablesScannerTest.java
License:Open Source License
@Test public void superNonSuspendableCallTest() { final String method = A.class.getName() + ".bar" + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(IA.class)); assertTrue(!suspependables.contains(method)); }
From source file:com.android.tools.layoutlib.create.DelegateMethodAdapter.java
License:Apache License
/** * Generates the new code for the method. * <p/>//from w ww . ja v a 2s .c o m * For native methods, this must be invoked directly by {@link DelegateClassAdapter} * (since they have no code to visit). * <p/> * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then * this method will be invoked from {@link MethodVisitor#visitEnd()}. */ public void generateCode() { /* * The goal is to generate a call to a static delegate method. * If this method is non-static, the first parameter will be 'this'. * All the parameters must be passed and then the eventual return type returned. * * Example, let's say we have a method such as * public void method_1(int a, Object b, ArrayList<String> c) { ... } * * We'll want to create a body that calls a delegate method like this: * TheClass_Delegate.method_1(this, a, b, c); * * If the method is non-static and the class name is an inner class (e.g. has $ in its * last segment), we want to push the 'this' of the outer class first: * OuterClass_InnerClass_Delegate.method_1( * OuterClass.this, * OuterClass$InnerClass.this, * a, b, c); * * Only one level of inner class is supported right now, for simplicity and because * we don't need more. * * The generated class name is the current class name with "_Delegate" appended to it. * One thing to realize is that we don't care about generics -- since generic types * are erased at runtime, they have no influence on the method name being called. */ // Add our annotation AnnotationVisitor aw = mParentVisitor.visitAnnotation( Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), true); // visible at runtime aw.visitEnd(); if (!mVisitCodeCalled) { // If this is a direct call to generateCode() as done by DelegateClassAdapter // for natives, visitCode() hasn't been called yet. mParentVisitor.visitCode(); mVisitCodeCalled = true; } ArrayList<Type> paramTypes = new ArrayList<Type>(); String delegateClassName = mClassName + DELEGATE_SUFFIX; boolean pushedArg0 = false; int maxStack = 0; // For an instance method (e.g. non-static), push the 'this' preceded // by the 'this' of any outer class, if any. if (!mIsStatic) { // Check if the last segment of the class name has inner an class. // Right now we only support one level of inner classes. int slash = mClassName.lastIndexOf('/'); int dol = mClassName.lastIndexOf('$'); if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) { String outerClass = mClassName.substring(0, dol); Type outerType = Type.getObjectType(outerClass); // Change a delegate class name to "com/foo/Outer_Inner_Delegate" delegateClassName = delegateClassName.replace('$', '_'); // The first-level inner class has a package-protected member called 'this$0' // that points to the outer class. // Push this.getField("this$0") on the call stack. mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this mParentVisitor.visitFieldInsn(Opcodes.GETFIELD, mClassName, // class where the field is defined "this$0", // field name outerType.getDescriptor()); // type of the field maxStack++; paramTypes.add(outerType); } // Push "this" for the instance method, which is always ALOAD 0 mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); maxStack++; pushedArg0 = true; paramTypes.add(Type.getObjectType(mClassName)); } // Push all other arguments. Start at arg 1 if we already pushed 'this' above. Type[] argTypes = Type.getArgumentTypes(mDesc); int maxLocals = pushedArg0 ? 1 : 0; for (Type t : argTypes) { int size = t.getSize(); mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); maxLocals += size; maxStack += size; paramTypes.add(t); } // Construct the descriptor of the delegate based on the parameters // we pushed on the call stack. The return type remains unchanged. String desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), paramTypes.toArray(new Type[paramTypes.size()])); // Invoke the static delegate mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName, mMethodName, desc); Type returnType = Type.getReturnType(mDesc); mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); mParentVisitor.visitMaxs(maxStack, maxLocals); mParentVisitor.visitEnd(); // For debugging now. Maybe we should collect these and store them in // a text file for helping create the delegates. We could also compare // the text file to a golden and break the build on unsupported changes // or regressions. Even better we could fancy-print something that looks // like the expected Java method declaration. mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc); }
From source file:com.android.tools.layoutlib.create.DelegateMethodAdapter2.java
License:Apache License
/** * Generates the new code for the method. * <p/>// ww w . j a va 2 s.c o m * For native methods, this must be invoked directly by {@link DelegateClassAdapter} * (since they have no code to visit). * <p/> * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then * this method will be invoked from {@link MethodVisitor#visitEnd()}. */ public void generateDelegateCode() { /* * The goal is to generate a call to a static delegate method. * If this method is non-static, the first parameter will be 'this'. * All the parameters must be passed and then the eventual return type returned. * * Example, let's say we have a method such as * public void myMethod(int a, Object b, ArrayList<String> c) { ... } * * We'll want to create a body that calls a delegate method like this: * TheClass_Delegate.myMethod(this, a, b, c); * * If the method is non-static and the class name is an inner class (e.g. has $ in its * last segment), we want to push the 'this' of the outer class first: * OuterClass_InnerClass_Delegate.myMethod( * OuterClass.this, * OuterClass$InnerClass.this, * a, b, c); * * Only one level of inner class is supported right now, for simplicity and because * we don't need more. * * The generated class name is the current class name with "_Delegate" appended to it. * One thing to realize is that we don't care about generics -- since generic types * are erased at build time, they have no influence on the method name being called. */ // Add our annotation AnnotationVisitor aw = mDelWriter.visitAnnotation( Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), true); // visible at runtime if (aw != null) { aw.visitEnd(); } mDelWriter.visitCode(); if (mDelegateLineNumber != null) { Object[] p = mDelegateLineNumber; mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]); } ArrayList<Type> paramTypes = new ArrayList<Type>(); String delegateClassName = mClassName + DELEGATE_SUFFIX; boolean pushedArg0 = false; int maxStack = 0; // Check if the last segment of the class name has inner an class. // Right now we only support one level of inner classes. Type outerType = null; int slash = mClassName.lastIndexOf('/'); int dol = mClassName.lastIndexOf('$'); if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) { String outerClass = mClassName.substring(0, dol); outerType = Type.getObjectType(outerClass); // Change a delegate class name to "com/foo/Outer_Inner_Delegate" delegateClassName = delegateClassName.replace('$', '_'); } // For an instance method (e.g. non-static), push the 'this' preceded // by the 'this' of any outer class, if any. if (!mIsStatic) { if (outerType != null) { // The first-level inner class has a package-protected member called 'this$0' // that points to the outer class. // Push this.getField("this$0") on the call stack. mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this mDelWriter.visitFieldInsn(Opcodes.GETFIELD, mClassName, // class where the field is defined "this$0", // field name outerType.getDescriptor()); // type of the field maxStack++; paramTypes.add(outerType); } // Push "this" for the instance method, which is always ALOAD 0 mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); maxStack++; pushedArg0 = true; paramTypes.add(Type.getObjectType(mClassName)); } // Push all other arguments. Start at arg 1 if we already pushed 'this' above. Type[] argTypes = Type.getArgumentTypes(mDesc); int maxLocals = pushedArg0 ? 1 : 0; for (Type t : argTypes) { int size = t.getSize(); mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); maxLocals += size; maxStack += size; paramTypes.add(t); } // Construct the descriptor of the delegate based on the parameters // we pushed on the call stack. The return type remains unchanged. String desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), paramTypes.toArray(new Type[paramTypes.size()])); // Invoke the static delegate mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName, mMethodName, desc); Type returnType = Type.getReturnType(mDesc); mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); mDelWriter.visitMaxs(maxStack, maxLocals); mDelWriter.visitEnd(); // For debugging now. Maybe we should collect these and store them in // a text file for helping create the delegates. We could also compare // the text file to a golden and break the build on unsupported changes // or regressions. Even better we could fancy-print something that looks // like the expected Java method declaration. mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc); }