javah
from Netscape on the class files
whose methods you want to call. This will generate the header files
you'll need for C and C++ to call Java.
For example, say you want to call methods from the
java.lang.String
class. All you have to do is call:
% javah -jri java.lang.String(Be sure to have your classpath set properly, or use the
-classpath
option to javah
.) This will
generate the file java_lang_String.h
with accessors
macros defined for all methods and fields that are accessible.
javah
uses a naming convention to
create accessor macros for all fields defined for a Java class. This
convention prepends get_
or set_
followed by
the package and class name to the field name to define a getter or
setter. When the preprocessor symbol DEBUG
is defined, these names
declare functions (for better type checking) with signatures of the
form:
ResultType get_ClassName_fieldName(JRIEnv* env, ClassName* self); void set_ClassName_fieldName(JRIEnv* env, ClassName* self, ResultType value);Note that the class name itself denotes the type of the object whose field is to be accessed (the
self
parameter). The name of
the struct may also be used to avoid including additional header
files, e.g. struct ClassName*
.In C++, the type denoting the Java class becomes an actual C++ class with the same name. Fields can be accessed via inlined accessor methods of the form:
class ClassName : ... { ResultType // getter method fieldName(JRIEnv* env) { ... } void // setter method fieldName(JRIEnv* env, ResultType value) { ... } ... }So, for example, you can get the
count
field of the
String
class in C via:
jint cnt = get_java_lang_String_count(env, myString);or in C++:
jint cnt = myString->count(env);and set the
count
field in C via:
set_java_lang_String_count(env, myString, cnt);or in C++:
myString->count(env, cnt);The env parameter is passed to all Java methods and represents the current execution environment (the state of the Java interpreter).
Security Considerations
Note that the count
field of the String
is
private although we are able to access it anyway. This is because the
C and C++ interface to Java objects has unrestricted access
privileges. Although we could have made you jump through more hoops
to get private and protected information from Java, we didn't because
you can always cast and find it anyway. However, it is not advised to
access private fields or invoke private methods because other Java
implementations may not provide these members (as they are not part of
the contract published in the Java interface).
Static Fields
Static fields can be accessed in almost the same way as non-static
fields. Rather than accessing them from an instance, we access them
from a class object. So in Java where one might write:
PrintStream o = System.out;from C you would instead write:
java_lang_Class* systemClass = class_java_lang_System(env); java_io_PrintStream* o = java_lang_System_out(env, systemClass);or in C++:
java_lang_Class* systemClass = java_lang_System::_class(env); java_io_PrintStream* o = java_lang_System::out(env, systemClass);Note that although in C++ static Java object fields are accessed by static member functions, these member functions still have to be passed the Java class object. Although the class object could be looked up by the member function, passing it in eliminates possibile multiple lookups.
substring
method of the String
class can be
called as follows:
java_lang_String* sub = java_lang_String_substring(env, str, 5, 9);This call gets the substring of the string
str
from
characters 5 through 9 and assigns the result to the sub
variable.The above example also can be expressed in C++ as:
java_lang_String* sub = str->substring(env, 5, 9);
javah
attempts to deal with overloaded
methods in Java in a more graceful way than fully mangling the
name. Mangled names such as the way C++ member function names appear
to C are not mnemonic and are difficult to deal with.
Whenever javah
encounters more than one method with the
same name, it generates symbols in C (and C++) that have an index
appended to them for all but the first occurrance. For example, in the
String
class there are several methods with the name
indexOf
:
public int indexOf(int ch); public int indexOf(int ch, int fromIndex); public int indexOf(String str); public int indexOf(String str, int fromIndex);In C these are given the names (and signatures):
extern jint java_lang_String_indexOf(JRIEnv* env, java_lang_String* self, jint a); extern jint java_lang_String_indexOf_1(JRIEnv* env, java_lang_String* self, jint a, jint b); extern jint java_lang_String_indexOf_2(JRIEnv* env, java_lang_String* self, java_lang_String *a); extern jint java_lang_String_indexOf_3(JRIEnv* env, java_lang_String* self, java_lang_String *a, jint b);and in C++:
jint indexOf(JRIEnv* env, jint a); jint indexOf_1(JRIEnv* env, jint a, jint b); jint indexOf_2(JRIEnv* env, java_lang_String *a); jint indexOf_3(JRIEnv* env, java_lang_String *a, jint b);Here's a helpful hint: Put the overloaded method you want to use most from C or C++ before the other overloaded methods in the class definition. This will cause it's name to not have an index appended to it. Also, if you have a method implemented in Java and an overloaded declaration of a native method, put the native method first. It's the one you'll have to implement in your C code, and again you can avoid the appended index.
But what if I define a method called indexOf_1
? To
prevent this sort of collision, javah
mangles method
names containing underscores. All underscores get replaced by the
hexidecimal value 00005f
. Other Unicode characters also
get replaced in this way with their hexidecimal equivalent. This means
although the second overloaded version of indexOf
would
become indexOf_1
, a method explicitly named
indexOf_1
would become
indexOf_00005f1
. Bottom line: avoid underscores and
you'll be much happier.
Note that although you can call methods which are overloaded in
this way, you cannot define native methods with overloaded names. This
is currently a limitation of the Java 1.0 runtime.
Java Names for Primitive Types
What's a jint
? A jint
is the JRI way of
expressing a 32-bit signed integer value that works across all
platforms. The JRI defines similar C type names for all of Java's
scalar types. The following table shows the correspondence:
Java Type | C/C++ Type | Size |
boolean | jbool | 1 byte |
byte | jbyte | 1 byte |
char | jchar | 2 bytes |
short | jshort | 2 bytes |
int | jint | 4 bytes |
long | jlong | 8 bytes |
float | jfloat | 4 bytes |
double | jdouble | 8 bytes |
You should always use these JRI types when dealing with Java from C
and C++ rather than standard types like int
or
long
. This is the first step to ensuring that your code
will work across multiple platforms.
These JRI types are defined in the file jri_md.h
which is
included by the main JRI header file, jri.h
. These types
are conditionally defined for each architecture and compiler by
controlling numerous preprocessor symbols (including type sizes,
alignment, byte ordering, whether the compiler supports long
long
values, etc.).
You must set the right preprocessor symbols for your architecture
and compiler before including jri.h
. Look at
jri_md.h
to see what you'll need. The
jri_md.h
header also defines a set of cross-platform
macros for manipulating jlong
(long long
)
values.
What's this
The env
parameter?env
parameter represents the execution environment --
a JRIEnv*
that's passed to all JRI operations (and
consequently operations generated by javah
). It's your
handle on the Java interpreter. It serves two purposes:
So how do you get your hands on one of these execution environments in
the first place? The Navigator Plug-in API defines a new call,
NPN_GetJavaEnv
, that delivers one to you:
extern JRIEnv* NPN_GetJavaEnv(void);You can call this whenever you need to access the Java interpreter. The first time you call this, the interpreter may have to initialize itself if the browser hasn't already run a Java applet (so be patient).