* invocation handler's "invoke" method. The code is written * to the supplied stream. */ private void codeWrapArgument(Class type, int slot, DataOutputStream out) throws IOException { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); if (type == int.class || type == boolean.class || type == byte.class || type == char.class || type == short.class) { code_iload(slot, out); } else if (type == long.class) { code_lload(slot, out); } else if (type == float.class) { code_fload(slot, out); } else if (type == double.class) { code_dload(slot, out); } else { throw new AssertionError(); } out.writeByte(opc_invokestatic); out.writeShort(cp.getMethodRef( prim.wrapperClassName, "valueOf", prim.wrapperValueOfDesc)); } else { code_aload(slot, out); } } /** * Generate code for unwrapping a return value of the given * type from the invocation handler's "invoke" method (as type * Object) to its correct type. The code is written to the * supplied stream. */ private void codeUnwrapReturnValue(Class type, DataOutputStream out) throws IOException { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); out.writeByte(opc_checkcast); out.writeShort(cp.getClass(prim.wrapperClassName)); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( prim.wrapperClassName, prim.unwrapMethodName, prim.unwrapMethodDesc)); if (type == int.class || type == boolean.class || type == byte.class || type == char.class || type == short.class) { out.writeByte(opc_ireturn); } else if (type == long.class) { out.writeByte(opc_lreturn); } else if (type == float.class) { out.writeByte(opc_freturn); } else if (type == double.class) { out.writeByte(opc_dreturn); } else { throw new AssertionError(); } } else { out.writeByte(opc_checkcast); out.writeShort(cp.getClass(dotToSlash(type.getName()))); out.writeByte(opc_areturn); } } /** * Generate code for initializing the static field that stores * the Method object for this proxy method. The code is written * to the supplied stream. */ private void codeFieldInitialization(DataOutputStream out) throws IOException { codeClassForName(fromClass, out); code_ldc(cp.getString(methodName), out); code_ipush(parameterTypes.length, out); out.writeByte(opc_anewarray); out.writeShort(cp.getClass("java/lang/Class")); for (int i = 0; i < parameterTypes.length; i++) { out.writeByte(opc_dup); code_ipush(i, out); if (parameterTypes[i].isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]); out.writeByte(opc_getstatic); out.writeShort(cp.getFieldRef( prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); } else { codeClassForName(parameterTypes[i], out); } out.writeByte(opc_aastore); } out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)" + "Ljava/lang/reflect/Method;")); out.writeByte(opc_putstatic); out.writeShort(cp.getFieldRef( dotToSlash(className), methodFieldName, "Ljava/lang/reflect/Method;")); } } /** * Generate the constructor method for the proxy class. */ private MethodInfo generateConstructor() throws IOException { MethodInfo minfo = new MethodInfo( "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", ACC_PUBLIC); DataOutputStream out = new DataOutputStream(minfo.code); code_aload(0, out); code_aload(1, out); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( superclassName, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V")); out.writeByte(opc_return); minfo.maxStack = 10; minfo.maxLocals = 2; minfo.declaredExceptions = new short[0]; return minfo; } /** * Generate the static initializer method for the proxy class. */ private MethodInfo generateStaticInitializer() throws IOException { MethodInfo minfo = new MethodInfo( "<clinit>", "()V", ACC_STATIC); int localSlot0 = 1; short pc, tryBegin = 0, tryEnd; DataOutputStream out = new DataOutputStream(minfo.code); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { pm.codeFieldInitialization(out); } } out.writeByte(opc_return); tryEnd = pc = (short) minfo.code.size(); minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass("java/lang/NoSuchMethodException"))); code_astore(localSlot0, out); out.writeByte(opc_new); out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); out.writeByte(opc_dup); code_aload(localSlot0, out); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V")); out.writeByte(opc_athrow); pc = (short) minfo.code.size(); minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass("java/lang/ClassNotFoundException"))); code_astore(localSlot0, out); out.writeByte(opc_new); out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); out.writeByte(opc_dup); code_aload(localSlot0, out); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V")); out.writeByte(opc_athrow); if (minfo.code.size() > 65535) { throw new IllegalArgumentException("code size limit exceeded"); } minfo.maxStack = 10; minfo.maxLocals = (short) (localSlot0 + 1); minfo.declaredExceptions = new short[0]; return minfo; } /* * =============== Code Generation Utility Methods =============== */ /* * The following methods generate code for the load or store operation * indicated by their name for the given local variable. The code is * written to the supplied stream. */ private void code_iload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); } private void code_lload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); } private void code_fload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); } private void code_dload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); } private void code_aload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); } // private void code_istore(int lvar, DataOutputStream out)// throws IOException// {// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out);// } // private void code_lstore(int lvar, DataOutputStream out)// throws IOException// {// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out);// } // private void code_fstore(int lvar, DataOutputStream out)// throws IOException// {// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out);// } // private void code_dstore(int lvar, DataOutputStream out)// throws IOException// {// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out);// } private void code_astore(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); } /** * Generate code for a load or store instruction for the given local * variable. The code is written to the supplied stream. * * "opcode" indicates the opcode form of the desired load or store * instruction that takes an explicit local variable index, and * "opcode_0" indicates the corresponding form of the instruction * with the implicit index 0. */ private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, DataOutputStream out) throws IOException { assert lvar >= 0 && lvar <= 0xFFFF; if (lvar <= 3) { out.writeByte(opcode_0 + lvar); } else if (lvar <= 0xFF) { out.writeByte(opcode); out.writeByte(lvar & 0xFF); } else { /* * Use the "wide" instruction modifier for local variable * indexes that do not fit into an unsigned byte. */ out.writeByte(opc_wide); out.writeByte(opcode); out.writeShort(lvar & 0xFFFF); } } /** * Generate code for an "ldc" instruction for the given constant pool * index (the "ldc_w" instruction is used if the index does not fit * into an unsigned byte). The code is written to the supplied stream. */ private void code_ldc(int index, DataOutputStream out) throws IOException { assert index >= 0 && index <= 0xFFFF; if (index <= 0xFF) { out.writeByte(opc_ldc); out.writeByte(index & 0xFF); } else { out.writeByte(opc_ldc_w); out.writeShort(index & 0xFFFF); } } /** * Generate code to push a constant integer value on to the operand * stack, using the "iconst_<i>", "bipush", or "sipush" instructions * depending on the size of the value. The code is written to the * supplied stream. */ private void code_ipush(int value, DataOutputStream out) throws IOException { if (value >= -1 && value <= 5) { out.writeByte(opc_iconst_0 + value); } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { out.writeByte(opc_bipush); out.writeByte(value & 0xFF); } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { out.writeByte(opc_sipush); out.writeShort(value & 0xFFFF); } else { throw new AssertionError(); } } /** * Generate code to invoke the Class.forName with the name of the given * class to get its Class object at runtime. The code is written to * the supplied stream. Note that the code generated by this method * may caused the checked ClassNotFoundException to be thrown. */ private void codeClassForName(Class cl, DataOutputStream out) throws IOException { code_ldc(cp.getString(cl.getName()), out); out.writeByte(opc_invokestatic); out.writeShort(cp.getMethodRef( "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); } /* * ==================== General Utility Methods ==================== */ /** * Convert a fully qualified class name that uses '.' as the package * separator, the external representation used by the Java language * and APIs, to a fully qualified class name that uses '/' as the * package separator, the representation used in the class file * format (see JVMS section 4.2). */ private static String dotToSlash(String name) { return name.replace('.', '/'); } /** * Return the "method descriptor" string for a method with the given * parameter types and return type. See JVMS section 4.3.3. */ private static String getMethodDescriptor(Class[] parameterTypes, Class returnType) { return getParameterDescriptors(parameterTypes) + ((returnType == void.class) ? "V" : getFieldType(returnType)); } /** * Return the list of "parameter descriptor" strings enclosed in * parentheses corresponding to the given parameter types (in other * words, a method descriptor without a return descriptor). This * string is useful for constructing string keys for methods without * regard to their return type. */ private static String getParameterDescriptors(Class[] parameterTypes) { StringBuilder desc = new StringBuilder("("); for (int i = 0; i < parameterTypes.length; i++) { desc.append(getFieldType(parameterTypes[i])); } desc.append(')'); return desc.toString(); } /** * Return the "field type" string for the given type, appropriate for * a field descriptor, a parameter descriptor, or a return descriptor * other than "void". See JVMS section 4.3.2. */ private static String getFieldType(Class type) { if (type.isPrimitive()) { return PrimitiveTypeInfo.get(type).baseTypeString; } else if (type.isArray()) { /* * According to JLS 20.3.2, the getName() method on Class does * return the VM type descriptor format for array classes (only); * using that should be quicker than the otherwise obvious code: * * return "[" + getTypeDescriptor(type.getComponentType()); */ return type.getName().replace('.', '/'); } else { return "L" + dotToSlash(type.getName()) + ";"; } } /** * Returns a human-readable string representing the signature of a * method with the given name and parameter types. */ private static String getFriendlyMethodSignature(String name, Class[] parameterTypes) { StringBuilder sig = new StringBuilder(name); sig.append('('); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { sig.append(','); } Class parameterType = parameterTypes[i]; int dimensions = 0; while (parameterType.isArray()) { parameterType = parameterType.getComponentType(); dimensions++; } sig.append(parameterType.getName()); while (dimensions-- > 0) { sig.append("[]"); } } sig.append(')'); return sig.toString(); } /** * Return the number of abstract "words", or consecutive local variable * indexes, required to contain a value of the given type. See JVMSLines 1001–1500 of 2009. Previous lines Load more
* invocation handler's "invoke" method. The code is written * to the supplied stream. */ private void codeWrapArgument(Class type, int slot, DataOutputStream out) throws IOException { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); if (type == int.class || type == boolean.class || type == byte.class || type == char.class || type == short.class) { code_iload(slot, out); } else if (type == long.class) { code_lload(slot, out); } else if (type == float.class) { code_fload(slot, out); } else if (type == double.class) { code_dload(slot, out); } else { throw new AssertionError(); } out.writeByte(opc_invokestatic); out.writeShort(cp.getMethodRef( prim.wrapperClassName, "valueOf", prim.wrapperValueOfDesc)); } else { code_aload(slot, out); } } /** * Generate code for unwrapping a return value of the given * type from the invocation handler's "invoke" method (as type * Object) to its correct type. The code is written to the * supplied stream. */ private void codeUnwrapReturnValue(Class type, DataOutputStream out) throws IOException { if (type.isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); out.writeByte(opc_checkcast); out.writeShort(cp.getClass(prim.wrapperClassName)); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( prim.wrapperClassName, prim.unwrapMethodName, prim.unwrapMethodDesc)); if (type == int.class || type == boolean.class || type == byte.class || type == char.class || type == short.class) { out.writeByte(opc_ireturn); } else if (type == long.class) { out.writeByte(opc_lreturn); } else if (type == float.class) { out.writeByte(opc_freturn); } else if (type == double.class) { out.writeByte(opc_dreturn); } else { throw new AssertionError(); } } else { out.writeByte(opc_checkcast); out.writeShort(cp.getClass(dotToSlash(type.getName()))); out.writeByte(opc_areturn); } } /** * Generate code for initializing the static field that stores * the Method object for this proxy method. The code is written * to the supplied stream. */ private void codeFieldInitialization(DataOutputStream out) throws IOException { codeClassForName(fromClass, out); code_ldc(cp.getString(methodName), out); code_ipush(parameterTypes.length, out); out.writeByte(opc_anewarray); out.writeShort(cp.getClass("java/lang/Class")); for (int i = 0; i < parameterTypes.length; i++) { out.writeByte(opc_dup); code_ipush(i, out); if (parameterTypes[i].isPrimitive()) { PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]); out.writeByte(opc_getstatic); out.writeShort(cp.getFieldRef( prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); } else { codeClassForName(parameterTypes[i], out); } out.writeByte(opc_aastore); } out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)" + "Ljava/lang/reflect/Method;")); out.writeByte(opc_putstatic); out.writeShort(cp.getFieldRef( dotToSlash(className), methodFieldName, "Ljava/lang/reflect/Method;")); } } /** * Generate the constructor method for the proxy class. */ private MethodInfo generateConstructor() throws IOException { MethodInfo minfo = new MethodInfo( "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", ACC_PUBLIC); DataOutputStream out = new DataOutputStream(minfo.code); code_aload(0, out); code_aload(1, out); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( superclassName, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V")); out.writeByte(opc_return); minfo.maxStack = 10; minfo.maxLocals = 2; minfo.declaredExceptions = new short[0]; return minfo; } /** * Generate the static initializer method for the proxy class. */ private MethodInfo generateStaticInitializer() throws IOException { MethodInfo minfo = new MethodInfo( "<clinit>", "()V", ACC_STATIC); int localSlot0 = 1; short pc, tryBegin = 0, tryEnd; DataOutputStream out = new DataOutputStream(minfo.code); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { pm.codeFieldInitialization(out); } } out.writeByte(opc_return); tryEnd = pc = (short) minfo.code.size(); minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass("java/lang/NoSuchMethodException"))); code_astore(localSlot0, out); out.writeByte(opc_new); out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); out.writeByte(opc_dup); code_aload(localSlot0, out); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V")); out.writeByte(opc_athrow); pc = (short) minfo.code.size(); minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass("java/lang/ClassNotFoundException"))); code_astore(localSlot0, out); out.writeByte(opc_new); out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); out.writeByte(opc_dup); code_aload(localSlot0, out); out.writeByte(opc_invokevirtual); out.writeShort(cp.getMethodRef( "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V")); out.writeByte(opc_athrow); if (minfo.code.size() > 65535) { throw new IllegalArgumentException("code size limit exceeded"); } minfo.maxStack = 10; minfo.maxLocals = (short) (localSlot0 + 1); minfo.declaredExceptions = new short[0]; return minfo; } /* * =============== Code Generation Utility Methods =============== */ /* * The following methods generate code for the load or store operation * indicated by their name for the given local variable. The code is * written to the supplied stream. */ private void code_iload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); } private void code_lload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); } private void code_fload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); } private void code_dload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); } private void code_aload(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); } // private void code_istore(int lvar, DataOutputStream out) // throws IOException // { // codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out); // } // private void code_lstore(int lvar, DataOutputStream out) // throws IOException // { // codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out); // } // private void code_fstore(int lvar, DataOutputStream out) // throws IOException // { // codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out); // } // private void code_dstore(int lvar, DataOutputStream out) // throws IOException // { // codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out); // } private void code_astore(int lvar, DataOutputStream out) throws IOException { codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); } /** * Generate code for a load or store instruction for the given local * variable. The code is written to the supplied stream. * * "opcode" indicates the opcode form of the desired load or store * instruction that takes an explicit local variable index, and * "opcode_0" indicates the corresponding form of the instruction * with the implicit index 0. */ private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, DataOutputStream out) throws IOException { assert lvar >= 0 && lvar <= 0xFFFF; if (lvar <= 3) { out.writeByte(opcode_0 + lvar); } else if (lvar <= 0xFF) { out.writeByte(opcode); out.writeByte(lvar & 0xFF); } else { /* * Use the "wide" instruction modifier for local variable * indexes that do not fit into an unsigned byte. */ out.writeByte(opc_wide); out.writeByte(opcode); out.writeShort(lvar & 0xFFFF); } } /** * Generate code for an "ldc" instruction for the given constant pool * index (the "ldc_w" instruction is used if the index does not fit * into an unsigned byte). The code is written to the supplied stream. */ private void code_ldc(int index, DataOutputStream out) throws IOException { assert index >= 0 && index <= 0xFFFF; if (index <= 0xFF) { out.writeByte(opc_ldc); out.writeByte(index & 0xFF); } else { out.writeByte(opc_ldc_w); out.writeShort(index & 0xFFFF); } } /** * Generate code to push a constant integer value on to the operand * stack, using the "iconst_<i>", "bipush", or "sipush" instructions * depending on the size of the value. The code is written to the * supplied stream. */ private void code_ipush(int value, DataOutputStream out) throws IOException { if (value >= -1 && value <= 5) { out.writeByte(opc_iconst_0 + value); } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { out.writeByte(opc_bipush); out.writeByte(value & 0xFF); } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { out.writeByte(opc_sipush); out.writeShort(value & 0xFFFF); } else { throw new AssertionError(); } } /** * Generate code to invoke the Class.forName with the name of the given * class to get its Class object at runtime. The code is written to * the supplied stream. Note that the code generated by this method * may caused the checked ClassNotFoundException to be thrown. */ private void codeClassForName(Class cl, DataOutputStream out) throws IOException { code_ldc(cp.getString(cl.getName()), out); out.writeByte(opc_invokestatic); out.writeShort(cp.getMethodRef( "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); } /* * ==================== General Utility Methods ==================== */ /** * Convert a fully qualified class name that uses '.' as the package * separator, the external representation used by the Java language * and APIs, to a fully qualified class name that uses '/' as the * package separator, the representation used in the class file * format (see JVMS section 4.2). */ private static String dotToSlash(String name) { return name.replace('.', '/'); } /** * Return the "method descriptor" string for a method with the given * parameter types and return type. See JVMS section 4.3.3. */ private static String getMethodDescriptor(Class[] parameterTypes, Class returnType) { return getParameterDescriptors(parameterTypes) + ((returnType == void.class) ? "V" : getFieldType(returnType)); } /** * Return the list of "parameter descriptor" strings enclosed in * parentheses corresponding to the given parameter types (in other * words, a method descriptor without a return descriptor). This * string is useful for constructing string keys for methods without * regard to their return type. */ private static String getParameterDescriptors(Class[] parameterTypes) { StringBuilder desc = new StringBuilder("("); for (int i = 0; i < parameterTypes.length; i++) { desc.append(getFieldType(parameterTypes[i])); } desc.append(')'); return desc.toString(); } /** * Return the "field type" string for the given type, appropriate for * a field descriptor, a parameter descriptor, or a return descriptor * other than "void". See JVMS section 4.3.2. */ private static String getFieldType(Class type) { if (type.isPrimitive()) { return PrimitiveTypeInfo.get(type).baseTypeString; } else if (type.isArray()) { /* * According to JLS 20.3.2, the getName() method on Class does * return the VM type descriptor format for array classes (only); * using that should be quicker than the otherwise obvious code: * * return "[" + getTypeDescriptor(type.getComponentType()); */ return type.getName().replace('.', '/'); } else { return "L" + dotToSlash(type.getName()) + ";"; } } /** * Returns a human-readable string representing the signature of a * method with the given name and parameter types. */ private static String getFriendlyMethodSignature(String name, Class[] parameterTypes) { StringBuilder sig = new StringBuilder(name); sig.append('('); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { sig.append(','); } Class parameterType = parameterTypes[i]; int dimensions = 0; while (parameterType.isArray()) { parameterType = parameterType.getComponentType(); dimensions++; } sig.append(parameterType.getName()); while (dimensions-- > 0) { sig.append("[]"); } } sig.append(')'); return sig.toString(); } /** * Return the number of abstract "words", or consecutive local variable * indexes, required to contain a value of the given type. See JVMS