This chapter contains simple examples that illustrate the use of the K Native Interface.
Consider the following Java class that illustrates parameter passing from a Java program to a set of native methods:
package mypackage; public class ParameterPassing { private native void passOne(int i1); private native void passTwo(int i1, int i2); private native void passThree(int i1, int i2, int i3); public static void main(String[] args) { ParameterPassing p = new ParameterPassing(); p.passOne(2); p.passTwo(2, 4); p.passThree(2, 4, 8); } }
Below is the corresponding C programming language implementation of the native methods declared above:
#include <kni.h> #include <stdio.h> KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_ParameterPassing_passOne() { jint i1 = KNI_GetParameterAsInt(1); fprintf(stdout, “Parameter(s) passed: %ld\n”, i1); KNI_ReturnVoid(); } KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_ParameterPassing_passTwo() { jint i1 = KNI_GetParameterAsInt(1); jint i2 = KNI_GetParameterAsInt(2) fprintf(stdout, “Parameter(s) passed: %ld, %ld\n”, i1, i2); KNI_ReturnVoid(); } KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_ParameterPassing_passThree() { jint i1 = KNI_GetParameterAsInt(1); jint i2 = KNI_GetParameterAsInt(2); jint i3 = KNI_GetParameterAsInt(3); fprintf(stdout, “Parameter(s) passed: %ld, %ld, %ld\n”, i1, i2, i3); KNI_ReturnVoid(); }
In general, methods parameters of a primitive type can be accessed in native code by calling the appropriate KNI_GetParameterAs<Type>(jint index)
function with index
starting at one (1). Parameters are mapped from left to right, i.e., index
value 1 always refers to the leftmost parameter that has been passed on from the Java method to the native method. It is important to remember that long and double parameters occupy two entries in the operand stack.
Accessing the ‘this’ pointer in instance methods. In instance native methods (non-static native methods), the reference to the current instance (the value of the ‘this
’ pointer) can be obtained by calling function KNI_GetThisPointer(thisHandle), where thisHandle is a handle declared in the context of the current native method
. Handle thisHandle
will contain the value of the ‘this
’ pointer after calling this function.
this
’ pointer is not available in static native methods. The return value of the KNI_GetThisPointer
function is unspecified for static native methods.
Accessing the class pointer in static methods. In static methods, the class pointer can be obtained by calling function KNI_GetClassPointer(classHandle)
, where classHandle
is a handle declared in the context of the current native method. Handle classHandle
will contain the value of the class pointer after calling this function.
KNI_GetThisPointer
first to obtain a handle to an object pointer, and then calling KNI_GetObjectClass.
Every KNI function MUST call one of the KNI_ReturnVoid
, KNI_Return<Type>
or KNI_EndHandlesAndReturnObject
functions.
Even when a native function does not want to return any value back to the calling Java function, function KNI_ReturnVoid
must be called, or otherwise the operand stack of the Java virtual machine may become corrupted.
Below is a small native code fragment that illustrates how to return a primitive value from a native function.
#include <kni.h> KNIEXPORT KNI_RETURNTYPE_INT Java_mypackage_MyClass_myNativeFunction1() { // Return integer 123 to the calling Java method KNI_ReturnInt(123); }
Because all the object references in KNI are handles rather than direct object references, returning object refereces from KNI function is slightly more complicated than returning primitive values. Below is a small native code fragment that illustrates how to return an object reference from a native function. In this example, we will simply return the ‘this’ pointer that is implicitly passed to every instance (non-static) method in Java.
#include <kni.h> KNIEXPORT KNI_RETURNTYPE_OBJECT Java_mypackage_MyClass_myNativeFunction2() { KNI_StartHandles(1); KNI_DeclareHandle(objectHandle); // Read the ‘this’ pointer KNI_GetThisPointer(objectHandle); // Return the ‘this’ pointer to the calling Java method KNI_EndHandlesAndReturnObject(objectHandle); }
If a native function wants to return a NULL pointer back to the calling Java function, the object handle must be set to NULL explicitly by calling KNI_ReleaseHandle
. Below is a small example.
#include <kni.h> KNIEXPORT KNI_RETURNTYPE_OBJECT Java_mypackage_MyClass_myNativeFunction3() { KNI_StartHandles(1); KNI_DeclareHandle(objectHandle); // Set the handle explicitly to NULL KNI_ReleaseHandle(objectHandle); // Return the null reference to the calling Java method KNI_EndHandlesAndReturnObject(objectHandle); }
The Java programming language supports two kinds of fields. Each instance of a class has its own copy of the instance fields of the class, whereas all instances of a class share the static fields of the class.
Field access is a two-step process. For instance fields, you first call KNI_GetFieldID
to obtain the field ID for the given class reference, field name, and field descriptor (refer to Section 4.3.3 “Field Descriptors” for an overview of field descriptors):
Once you have obtained the field ID, you can pass the object reference and the field ID to the appropriate instance field access function:
For static fields, the procedure is similar, except that a separate set of functions is used:
KNI_GetStaticFieldID
for static fields instead of KNI_GetFieldID
for instance fields. KNI_GetStaticFieldID
and KNI_GetFieldID
have the same return type jfieldID
.fid = KNI_GetStaticFieldID(classHandle, "staticCount", "I"); jint staticCount = KNI_GetStaticIntField(classHandle, fid);
Remember that when you access a field of a reference type (a field that contains an object instead of a primitive value), the object reference will be returned as a handle. Below is a small example:
This function would read the instance field represented by fid, and assign the value of that field to toHandle
.
Let us take a look at an example program that illustrates how to access instance fields from a native method implementation.
package mypackage; public class InstanceFieldAccess { private int value; private native void accessFieldNatively(); public static void main(String[] args) { InstanceFieldAccess p = new InstanceFieldAccess(); p.value = 100; p.accessFieldNatively(); System.out.println("In Java:"); System.out.println(" Value = " + p.value); } }
The InstanceFieldAccess
class defines an instance field value
. The main
method creates an object of this class, sets the instance field, and then calls the native method InstanceFieldAccess.accessFieldNatively
. As we will see shortly, the native method prints to the standard output the value of the instance field.
Below is the implementation of the native method mypackage.InstanceFieldAccess.accessFieldNatively
.
#include <kni.h> #include <stdio.h> KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_InstanceFieldAccess_accessFieldNatively() { /* Declare handles */ KNI_StartHandles(2); KNI_DeclareHandle(objectHandle); KNI_DeclareHandle(classHandle); /* Get ‘this’ pointer */ KNI_GetThisPointer(objectHandle); /* Get instance’s class */ KNI_GetObjectClass(objectHandle, classHandle); /* Get field id and value */ jfieldID fid = KNI_GetFieldID(classHandle, "value", "I"); jint value = KNI_GetIntField(objectHandle, fid); /* Print field value */ fprintf(stdout, "In C:\n Value = %ld\n", value); KNI_EndHandles(); KNI_ReturnVoid(); }
Interpreting the InstanceFieldAccess
class with the KVM runtime interpreter produces the following output:
In C:
Value = 100
In Java:
Value = 100
Accessing static fields is similar to accessing instance fields. Let us take a look at a minor variation of the InstanceFieldAccess
example:
package mypackage; public class StaticFieldAccess { private static int value; private native void accessFieldNatively(); public static void main(String[] args) { StaticFieldAccess p = new StaticFieldAccess(); value = 100; p.accessFieldNatively(); System.out.println("In Java: "); System.out.println(" Value = " + value); } }
The StaticFieldAccess
class defines a static integer field value
. The StaticFieldAccess.main
method creates an object, initializes the static field, and then calls the native method StaticFieldAccess.accessFieldNatively
. The native method prints to the standard output the value of the static field and then sets the field to a new value. To verify that the field has indeed changed, the program prints the static field value again after the native method returns.
Below is the implementation of the native method StaticFieldAccess.accessFieldNatively
.
#include <kni.h> #include <stdio.h> KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_StaticFieldAccess_accessFieldNatively() { /* Declare handle */ KNI_StartHandles(1); KNI_DeclareHandle(classHandle); /* Get class pointer */ KNI_GetClassPointer(classHandle); /* Get “I” “value” field id and its value */ jfieldID fid = KNI_GetStaticFieldID(classHandle, "value", "I"); jint value = KNI_GetStaticIntField(classHandle, fid); /* Print “I” “value” field */ fprintf(stdout, "In C:\n Value = %ld\n", value); /* Change “I” “value” field */ KNI_SetStaticIntField(classHandle, fid, 200); KNI_EndHandles(); KNI_ReturnVoid(); }
Interpreting the StaticFieldAccess
class with the KVM runtime interpreter produces the following output:
In C:
Value = 100
In Java:
Value = 200
The KNI provides access to arrays of a primitive or reference type. For example, in the following code segment written in the The Java Programming Language1:
iarr
and farr
are primitive arrays while aiarr
is an array of a reference type.
Accessing arrays of a primitive type in a native method requires use of the family of KNI_Get/Set<Type>ArrayElement
where <Type>
is any one of the primitive types.
Let us look at a simple example. The main method of the following Java class calls a native method sumArrayNatively
that adds up the contents of an int
array.
package mypackage; public class SumIntArray { private native int sumArrayNatively(int[] arr); public static void main(String[] args) { SumIntArray p = new SumIntArray(); int arr[] = new int[10]; for (int i = 0; i < 10; i++) { arr[i] = i; } int sum = p.sumArrayNatively(arr); System.out.println(“sum: “ + sum); } }
The corresponding native C programming language code is shown below.
#include <kni.h> #include <stdio.h> KNIEXPORT KNI_RETURNTYPE_INT Java_mypackage_SumIntArray_sumArrayNatively() { jint i, sum = 0; /* Declare handle */ KNI_StartHandles(1); KNI_DeclareHandle(arrayHandle); /* Read parameter #1 to arrayHandle */ KNI_GetParameterAsObject(1, arrayHandle); /* Sum int array components */ for (i = 0; i < 10; i++) { sum += KNI_GetIntArrayElement(arrayHandle, i); } /* Set result sum */ KNI_EndHandles(); KNI_ReturnInt(sum); }
Remember that in KNI, arrays are represented by the jarray
reference type and its “subtypes” such as jintArray
. Just as a jstring
is not a C string type, neither is jarray
a C array type. One cannot implement the Java_IntArray_sumArray
native method by indirecting through a jarray
reference. Instead, one must use the proper KNI_Get<Type>ArrayElement
or KNI_Set<Type>ArrayElement
functions to access the array elements.
Below is a small sample program that illustrates string access using KNI.
package mypackage; public class StringAccess { private native void accessStringNatively(); public static void main(String[] args) { StringAccess p = new StringAccess(); p.accessStringNatively(“Parameter”); } }
The corresponding native code is below.
#include <kni.h> #include <stdio.h> KNIEXPORT KNI_RETURNTYPE_VOID Java_mypackage_StringAccess_accessStringNatively() { /* Allocate static buffer for the Unicode string */ jchar buffer[256]; jsize size; int i; /* Declare handle */ KNI_StartHandles(1); KNI_DeclareHandle(stringHandle); /* Read parameter #1 to stringHandle */ KNI_GetParameterAsObject(1, stringHandle); /* Get the length of the string */ size = KNI_GetStringLength(stringHandle); /* Copy the Java string to our own buffer (as Unicode) */ KNI_GetStringRegion(stringHandle, 0, size, buffer); /* Print the Unicode characters as 8-bit chars */ for (int i = 0; i < length; i++) { fprintf(stdout, "%c", (char)buffer[i]); } KNI_EndHandles(); KNI_ReturnVoid(); }
KNI_GetStringRegion
returns a Unicode string. This means that each returned character is 16 bits wide. It is important to take this into account when allocating buffer space for the returned string region.
KNI Specification K Native Interface (KNI), 1.0 |
Copyright © 2002 Sun Microsystems, Inc. All rights reserved.