Contents Previous Next

Chapter   7

Examples


This chapter contains simple examples that illustrate the use of the K Native Interface.

7.1 Parameter Passing

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.


Note – The ‘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.


Note – If you need to access the class pointer in an instance (non-static) method, it is recommended that you do this by calling KNI_GetThisPointer first to obtain a handle to an object pointer, and then calling KNI_GetObjectClass.

7.2 Returning Values from Native Functions

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.

7.2.1 Returning Primitive Values

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); 
} 

7.2.2 Returning Object References

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); 
} 

7.2.3 Returning Null Object References

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); 
} 

7.3 Accessing Fields

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.

7.3.1 General Procedure for Accessing Fields

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):

 
  fid = KNI_GetFieldID(classHandle, "count", "I"); 

Once you have obtained the field ID, you can pass the object reference and the field ID to the appropriate instance field access function:

  jint count = KNI_GetIntField(objectHandle, fid); 

For static fields, the procedure is similar, except that a separate set of functions is used:

  1. Call KNI_GetStaticFieldID for static fields instead of KNI_GetFieldID for instance fields. KNI_GetStaticFieldID and KNI_GetFieldID have the same return type jfieldID.
  2. Once a static field ID has been obtained, one can pass the class reference, instead of the object reference, to the appropriate static field access function.
  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:

KNI_GetObjectField(objectHandle, fid, toHandle); 

This function would read the instance field represented by fid, and assign the value of that field to toHandle.

7.3.2 Accessing Instance Fields

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

7.3.3 Accessing Static Fields

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

7.4 Accessing Arrays

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:

    int[] iarr; 
    float[] farr; 
    int[][] aiarr 

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.

7.5 Accessing Strings

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(); 
} 

Note – IMPORTANT: Remember that function 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.

1The Java™ Programming Language (Java Series), Second Edition by Ken Arnold and James Gosling (Addison- Wesley, 1998)

 


Contents Previous Next KNI Specification
K Native Interface (KNI), 1.0