Skip to content

Latest commit

 

History

History
166 lines (125 loc) · 5.41 KB

jni.md

File metadata and controls

166 lines (125 loc) · 5.41 KB

Overall Design

The JNI interface pointer is only valid in the current thread. A native method, therefore, must not pass the interface pointer from one thread to another. A VM implementing the JNI may allocate and store thread-local data in the area pointed to by the JNI interface pointer.

Design Overview Invocation API

Invoke native methods in Java

  1. Add a "native" keyword before the method declaration in class declaration
  2. Use javah to build C-style header files (javah automatically includes this file in the application header files)
  3. Implement the method in C/C++ code
  4. Compile the native method into a dynamically linked library
  5. Load generated library in Java code
  6. Call native method

A code snippet

/* 1: in java */
public native void sayHello();

/* 2: header generated by javah, com_test_TestNative.h */
/* Method'name always starts with Java */
JNIEXPORT void JNICALL Java_com_test_TestNative_sayHello (JNIEnv *, jobject);

/* 3: in c++ */
#include"com_test_TestNative.h"
...

JNIEXPORT void JNICALL Java_com_test_TestNative_sayHello(JNIEnv *env, jobject obj) {
    ...
}

/* 4: compile to dynamically linked library (platform dependent) */
gcc -o libNativeCode.so -lc -shared \
    -I/usr/local/jdk1.6.0_03/include \
    -I/usr/local/jdk1.6.0_03/include/linux com_test_TestNative_sayHello.c

/* in java */
public static void main(String[] args) {
    /* 5: load library */
    System.loadLibrary("NativeCode");
    TestNative tNative = new TestNative();
    /* 6: call native method */
    tNative.sayHello();
}

Invoke Java methods in native code

An example from the JNI Invocation API

    #include <jni.h>       /* where everything is defined */
    ...
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/usr/lib/java";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
    env->CallStaticVoidMethod(cls, mid, 100);
    /* We are done. */
    jvm->DestroyJavaVM();

Type Mapping

Primitive type

Reference type

Native Method Arguments

  • JNIEnv *env: Is a pointer that points to another pointer pointing to a function table (array of pointer). Each entry in this function table points to a JNI function. These are the functions we are going to use for type conversion
  • The second argument is different depending on whether the native method is a static method or an instance method
    • Instance method: It will be a jobject argument which is a reference to the object on which the method is invoked
    • Static method: It will be a jclass argument which is a reference to the class in which the method is define

Naming convention in Magnolia VM

Lowercase "jclass" is a type defined in JNI specification. Uppercase "JClass" is a representation of C++ class in Magnolia VM.

Some jni structures are defined as opaque structures to hide definition, there are a couple of ways to define an opaque structure. HotSpot defines them in the following fashion:

// foo.h
typedef struct _foo * fooRef;
void doStuff(fooRef f);

// foo.c
struct _foo {
    int x;
    int y;
};

C vs. C++

The implementation (and usage) of JNI interface has a slight difference depending on the native language being used.

JNIEnv and JavaVM

  • C

    • (*env)->method(env, args)
    • (*vm)->method(vm, args)
  • C++

    • env->method(args)
    • vm->method(args)

The C++'s style is compatible with C style by placing the pointers as the first member is C++ struct.

In header file:

struct _JNIEnv;
struct _JavaVM;

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;                                 // C++, the very first member of JNIEnv is JNINativeInterface, makes it compatible with C style definition
typedef _JavaVM JavaVM;                                 // C++

#else
typedef const struct JNINativeInterface* JNIEnv;        // C
typedef const struct JNIInvokeInterface* JavaVM;        // C

#endif

Refer to JNI.h for the actual defintion of JNINativeInterface, etc.

jclass

In C, jclass is a void* while in C++ jclass is an empty class.

#ifdef __cplusplus
/*Reference types, in C++*/
class _jobject {};
class _jclass : public _jobject {};
typedef _jclass*        jclass;

#else
/*Reference types, in C.*/
typedef void*           jobject;
typedef jobject         jclass;

#endif

Note that the jclass returned from a native call cannot be reused.