Month: July 2016

How to generate JNI based method Signature?

Posted on

The term method signature is not new to any of the developers. The moment you hear the term ‘method signature’ you will be thinking about something like follows.

public void print();

When using a general programming language as Java, C++, Python we start writing a method using a method signature, which includes a method name, return type and a parameter list wrapped in round brackets ‘()’.

But when you are working with Javassist, the byte code instrumentation framework, you may want to tell the JVM to access a specific method (CtMethod) of a given Class (CtClass) during the run time.  [If you are not familiar with bytecode instrumentation and Javassist please visit here for more information.]

Suppose we have a simple test class, ‘TestSignature’ with three overloaded methods (named as testMethod).

TestSignature.java

package test.signature;

public class TestSignature {
public void testMethod(){
//statements
}

public String testMethod(String x){
//statements
return x;
}

public void testMethod(String x, int y){
//statements
}

public int testMethod(String[] x){
//statements
return x.length;
}
}

A call to getDeclaredMethods() in Javassist API with the method name as testMethod’ will return all the methods with the given name as a CtMethod array. In order to pick the right method you have to go an extra mile by iterating through the array. But, if you know the JNI (Java Native Interface) type signature of the method, you can get the exact method with single call to getMethod() in Javassist API.

The JNI type method signature can be derived by following the naming convention below. The set of special notations used are,

Type Signature Java Type
Z Boolean
B byte
C char
S short
I int
J long
F float
D double
V void
L object
[ array

A method signature derived using these notations is written in the reverse order compared to the normal format. It starts with the list of parameters enclosed in round brackets and ends with the return type [ (parameter_types) return_type ]. It does not contain the method name. Some special points to note are,

  • If the parameter/return value is an object, use the fully qualified class name start with L and end with a semicolon (;). E.g: Ljava/lang/String;
  • If the parameter/return value is an array, start with a square bracket ‘[‘ which is followed by the above notations or the fully qualified name. E.g: [I or [java/lang/String;

If you follow these instructions you can derive the signature of any given method easily as shown below.

public void testMethod(String name)
    (Ljava/lang/String;)V   // derived signature

But still, there is a chance of making a mistake while trying to convert it to JNI format. Therefore, Java has provided a tool to automatically generate signatures of all the methods in a given class using a single command. The javap tool will make your life lot easier with javap -s command followed by the fully qualified class name.

Steps to be followed to generate the method signatures.

  • Write the Java class and save as a java file (TestSignature.java).
  • Compile your java file using the command line (javac test.signature.TestSignature) or  using the IDE.
  • Go to the folder which contains your .class files and run the command javap -s test.signature.TestSignature.
signature2
Output of  ‘javap -s test.signature.TestSignature’

Note:

  • If you are going to the exact location of the .class file, use the class name instead of using the fully qualified name)
  • You may use the above command if you have only the .class files too. But you should have all the depending classes used by the respective class, in order to generate the signature.

Well, that’s it for the topic. Hope you will find this article useful if you are stuck with a java agent and instrumenting overloaded methods. Till the next article, have a nice time … 🙂