Search code examples
javaandroidandroid-ndkjava-native-interface

How to get JNI-style signature string for a field or method?


I am trying to inspect a Java class and automatically retrieve all of its methods and fields in a way that I can invoke them via JNI on Android. However, I can't figure out how to actually get the textual signature string programmatically; I know how to get it via javap -s from the command line but I would like to get it from within my program.

The closest I can find is using clazz.getMethods() to get the individual methods, and then Method.toGenericString() to get a human-readable string, but that string is formatted like the Java code signature (e.g. public int someMethod(int foo, java.lang.String bar)) when I really just want the JNI-style signature (e.g. (ILjava/lang/String;)I), as I will need that string to call getMethodID() from JNI and I already have a perfectly-good parser that parses those JNI signatures in the first place.

I could just manually maintain bindings for the classes I actually care about accessing from JNI, but I'd much rather just have everything handled as automatically as possible; less code is better, after all.

I did find ASM which provides helper methods for this, but it appears to be a bytecode generation framework and I am not sure if it will be compatible with Android.

Or, in other words, given the following class:

class SomeClass {
    String foo(int bar) { return "This is foo " + bar; }
}

I want to create a class called ClassInspector which can do something like:

ClassInspector ci = new ClassInspector(SomeClass.class);
int n = ci.getMethodCount(); // returns 1
String name = ci.getMethodName(0); // returns "foo"
String sig = ci.getMethodSignature(0); // returns "(I)Ljava/lang/string;"

Which is to say that I want to be able to get the output of javap -s from within Java, so that I can call Java methods from the native side.


Solution

  • I had a similar need and found a solution by digging through JavapTask.java from the source code of the javap tool.

    Here is a snippet that does what you want:

    public class Test {
        String foo(int bar) { return "This is foo " + bar; }
    
        public static void main(String[] args) {
            Class<?> c = Test.class;
            java.lang.reflect.Method methods[] = c.getDeclaredMethods();
            System.out.println("Class : " + c.getName());
            System.out.println("Number of methods : " + methods.length);
            for (java.lang.reflect.Method m : methods) {
                java.lang.invoke.MethodType mt = java.lang.invoke.MethodType.methodType(m.getReturnType(), m.getParameterTypes());
                System.out.println(m.getName() + " : " + mt.toMethodDescriptorString());
            }
        }
    }
    

    It prints out the following:

    Class : Test
    Number of methods : 2
    main : ([Ljava/lang/String;)V
    foo : (I)Ljava/lang/String;