Implementing Native Methods |
Now that you've got a handle to a Java object in your native method, what can you do with it? You can use it to access the object's member variables. But first, you have to dereference the handle.Dereferencing the Handle
You apply theunhand()
macro to the object handle to dereference it. When you dereference a handle you get a pointer to a Cstruct
that parallels the Java object's structure. For example, to dereference the byte array, namedb
, passed intoInputFile_read()
function you use theunhand()
macro like this:unhand(b)unhand()
returns a pointer to a C structure. You can use normal C syntax to access the elements in the structure. The elements in the structure parallel the member variables of the objects. Thus, you can access the member variables of an object through the pointer returned fromunhand()
.
Note: Theunhand()
macro is defined in the header fileinterpreter.h
, which is included inStubPreamble.h
.
Accessing the Object's Member Variables
You can use the pointer returned fromunhand()
like any other Cstruct
pointer, accessing its members with the->
operator.The
b
argument in theInputFile_read()
function is a byte array. The structure in C that maps to Java arrays contains an element namedbody
which is a C array ofchar
. TheInputFile_read()
function gets a pointer to thebody
element ofb
with this code:Now that thechar *data = unhand(b)->body;InputFile_read()
function has a C pointer to the body of the array, it can gleefully read bytes into it, which it does with this line of code:Theactual = read(unhand(this)->fd, data, actual);struct
members have the same names as the corresponding variables in the Java class. So you can use this same mechanism to access an InputFile object's member variables. Indeed, theInputFile_open()
function uses this mechanism to access an InputFile object'spath
andfd
variables:Note that the data type of a variable in the Clong InputFile_open(struct HInputFile *this) { int fd; char buf[MAXPATHLEN]; javaString2CString(unhand(this)->path, buf, sizeof(buf)); convertPath(buf); fd = open(buf, O_RDONLY); if (fd < 0) return(FALSE); unhand(this)->fd = fd; return(TRUE); }struct
is the nearest matching C data type to the Java type of the member variable in the object. Be sure to declare and use the structure elements with the correct data types.Calling Java Methods from a Native Method
You can call a Java object's methods from a native method, but you use a different mechanism for that than you use for accessing its member variables. For example, thisdoes not work. Rather you use one of the utility functions declared inptr->methodname(); // doesn't workinterpreter.h
for this purpose.
execute_java_dynamic_method()
- calls an instance method from a native method
execute_java_static_method()
- calls a class method from a native method
execute_java_constructor()
- creates a new Java object from within a native method
The character-replacement program doesn't use any of these methods. Let's look at another example that does. The new example program is a collection of methods that illustrate how you can perform various tasks from within a native method. The example program, NativeExample, was generously provided to us by Thomas Ball of the Java team.
Let's focus now on the
doubleUp()
method in the NativeExample class. The implementation of thedoubleUp()
method usesexecute_java_constructor()
to create an object from within a native method, and thenexecute_java_dynamic_method()
to call one of the object's instance methods. This method returns the object it created.On the Java side,
doubleUp()
is declared like this:On the C side,native NativeExample doubleUp();doubleUp()
is implemented like this:The interesting bits of thestruct HNativeExample * NativeExample_doubleUp(struct HNativeExample *hInst) { HNativeExample *hNewInst; long twoX; hNewInst = (HNativeExample *)execute_java_constructor( 0, "NativeExample", 0, "(I)", unhand(unhand(hInst)->myValue)->value); twoX = (long)execute_java_dynamic_method( 0, (HObject *)hNewInst, "twoTimes", "()I"); unhand(unhand(hNewInst)->myValue)->value = twoX; return hNewInst; }doubleUp()
function are in bold.Calling a Java Constructor
Let's look at the first bold line in the code above--the call toexecute_java_constructor()
. Theexecute_java_constructor()
function requires four arguments, but may have more.The first argument to
execute_java_constructor()
is represents the execution context. For now, you should use use0
for the value of this argument to tell the Java runtime system to use the current execution context. In future releases of Java this argument won't be necessary and will likely be removed.The second argument is the name of the class you want to instantiate. The example creates a new NativeExample object, so the value of the second argument in that example is the string "NativeExample".
The third argument is the class structure for the class you are instantiating. This argument is redundant because it provides the same information as the second argument--the class to instantiate. However, the
execute_java_constructor()
function must do a class look up if you provide only the class name in the second argument and use0
for the third argument (as shown in the example). This can cause performance degradation if you are constructing a lot of objects from the same class. If performance is important to you, you can avoid unnecessary class lookups by providing a value for this argument with theFindClass()
function defined ininterpreter.h
.You use the fourth argument to specify the Java constructor that you want to invoke. The name of the Java constructor is the name of the class you are instantiating--
NativeExample()
in the example. However, because of method name overloading, you need to be more specific and indicate exactly which constructor you want by specifying the number and type of its arguments. You will also use signature arguments when callingexecute_java_dynamic_method()
andexecute_java_static_method()
.The example uses the following signature argument, which specifies the NativeExample constructor that takes one integer argument:
(I)The general form of a signature argument is:
arguments is a series of single keyletters that indicate the data type for an argument. The number of keyletters indicates the number of arguments. returnvalue is a keyletter that indicates the data type of the return value. The keyletters are defined in(arguments)returnvaluesignature.h
. Look in that file to find the keyletters that identify other data types such asfloat
,char
and so on. Note that a signature argument used withexecute_java_constructor()
cannot indicate a return value as Java constructors don't return a value.The general form of
execute_java_constructor()
is:The total number of arguments to the constructor is determined by the signature argument. The signature also indicates which of the classname constructors is called. This function returns the new object, or null if there is an error.HObject *execute_java_constructor(ExecEnv *env, char *classname, ClassClass *cb, char *signature, ...);Calling an Instance Method
After thedoubleUp()
function creates a new NativeExample object withexecute_java_constructor()
, it calls the object'stwoTimes()
instance method:ThetwoX = (long)execute_java_dynamic_method( 0, (HObject *)hNewInst, "twoTimes", "()I");execute_java_dynamic_method()
function requires four arguments, but may have more depending on which method you are calling.The first argument to
execute_java_dynamic_method()
is the same as the first argument toexecute_java_constructor()
--it's the execution context. Again, you should use0
for the value of this argument to tell the Java runtime system to use the current execution context. In future releases of Java this argument won't be necessary and will likely be removed.The second argument is the object whose instance method you want to execute. The third argument is the name of the instance method to execute. And finally, the fourth argument is the signature of the instance method. As with
execute_java_constructor()
the signature argument indicates if there are any remaining arguments, how many, and what type. You formulate the signature argument forexecute_java_dynamic_method()
as you did previously withexecute_java_constructor()
.The general form of
execute_java_dynamic_method()
is:long execute_java_dynamic_method(ExecEnv *env, HObject *obj, char *method_name, char *signature, ...);Calling a Class Method
To call a class method from a native method use:This function is analogous tolong execute_java_static_method(ExecEnv *env, ClassClass *cb, char *method_name, char *signature, ...);execute_java_dynamic_method
above, except that you provide a class structure instead of an object. You can get the class structure using theFindClass()
function defined ininterpreter.h
.
Implementing Native Methods |