ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Mark Bednarczyk" <voyte...@yahoo.com>
Subject Writing new task: javani
Date Tue, 27 Apr 2010 16:04:23 GMT
I'm writing a new task "javani" and want to see if there was any duplicate
effort with what I'm proposing.

Summary:
The task named 'javani' is a java preprocessor for generating wrapper
functions in C. Its input is a compiled java classfile, and its output
either a C or header file to be compiled, or both. The task expands on what
existing 'javah' task performs by generating C wrapper functions which
automate some common tasks usually performed manually using JNI C
programming interface.

The task can generate source C code (both C and header files), which needs
to be further compiled by a C compiler. The task can be instructed to
generate 2 types of output:

1) A header file, which provides C declarations for functions and JNI to
java mappings

2) A C source file containing wrapper functions which can be used to call on
any java method from C. The code generator takes care of maintaining/caching
JNI method and field IDs.

Typically stub and wrapper C functions do not mix and are thus output to
different C files. Stub files only need to be generated once, while wrapper
functions can be regenerated every time ant build compiles the code.

Easily create wrapper functions for any java class (including standard JRE
classes). This saves the time of trying to manually lookup and cache JNI
field and method IDs.  You call on C functions only supplying the JNIEnv and
jobject or jclass parameters and any other parameters declared in the java
method signature.

Here is an example java class:

package org.jnetsoft.jni.stub;
public class MyClass {
  public void method1() { }
  public int method2(char c, byte b, short s, int i, long l, String str)
{return 0;}
  private int field1;
}

And wrapper functions generated:
--------------------------------------------------------------------------
#include <jni.h>
#include "org_jnetsoft_jni_stub_MyClass.h"

org_jnetsoft_jni_stub_MyClass_class_t org_jnetsoft_jni_stub_MyClass_class;

/*
 * Class:     org.jnetsoft.jni.stub.MyClass
 * Method:    onClassLoad
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_jnetsoft_jni_stub_MyClass_onClassLoad
  (JNIEnv *env, jclass clazz) {

	org_jnetsoft_jni_stub_MyClass_class_t *c =
&org_jnetsoft_jni_stub_MyClass_class;

	memset(c, sizeof(org_jnetsoft_jni_stub_MyClass_class_t), 0);

	c->clazz = (*env)->NewGlobalRef(env, clazz);

	c->method1	= (*env)->GetMethodID(env, clazz, "method1", "()V");
	c->method2	= (*env)->GetMethodID(env, clazz, "method2",
"(CBSIJLjava/lang/String;)I");

	c->field1	= (*env)->GetFieldID(env, clazz, "field1", "I");
}

/*
 * Class:     org.jnetsoft.jni.stub.MyClass
 * Method:    onClassUnload
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_jnetsoft_jni_stub_MyClass_onClassUnload
  (JNIEnv *env, jclass clazz) {

	org_jnetsoft_jni_stub_MyClass_class_t *c =
&org_jnetsoft_jni_stub_MyClass_class;

	(*env)->DeleteGlobalRef(env, c->clazz);
}

/*
 * Class:     org.jnetsoft.jni.stub.MyClass
 * Method:    method1
 * Types:     ()
 * Signature: ()V
 */
void org_jnetsoft_jni_stub_MyClass_method1
  (JNIEnv *env, jobject obj) {
	(*env)->CallVoidMethod(env, obj,
org_jnetsoft_jni_stub_MyClass_class.method1);
}

/*
 * Class:     org.jnetsoft.jni.stub.MyClass
 * Method:    method2
 * Types:     (char, byte, short, int, long, String, )
 * Signature: (CBSIJLjava/lang/String;)I
 */
jint org_jnetsoft_jni_stub_MyClass_method2
  (JNIEnv *env, jobject obj, jchar arg1, jbyte arg2, jshort arg3, jint arg4,
jlong arg6, jobject arg7) {
	return (*env)->CallIntMethod(env, obj,
org_jnetsoft_jni_stub_MyClass_class.method2, arg1, arg2, arg3, arg4, arg6,
arg7);
}
[truncated...]
--------------------------------------------------------------------------

And so forth. The code generator is working already (as seen above), I
started putting it into an ANT task and figured this would be a good time to
check the community.

The generator has no external dependencies and is pure java. The current
code generator can also generate C "stub" functions for all declared
'native' methods in java class, but that functionality would be ommitted
from the task. The long 'namespace' prefix for every function can be
overwritten with any user specified one. For example in the above sample,
the namespace prefix used 'org_jnetsoft_jni_stub' could be replaced by
'javani' task to user supplied one such as 'stub' thus greatly shortening
the function and ID names making them more convenient to utilize in user's
code.

Here is a typical scenario I would envision for usage with this task. A
programmer would choose which java calls need to be made on java classes
from native C or C++ code. He would add a list of those classes to his ANT
build script, through a refid or some other list of classes (typically an
external .properties file). The build process would proceed in roughly the
following order:

1) compile user java code to classfiles

2) invoke javah to generate stub header files for all native java functions
(as normally done)

3) invoke javani to generate wrapper functions and header files for all
classes and java methods which are expected to be invoked from user's native
code. User would declare those classes in a list.

4) compile with a C compiler all the newly generated wrapper functions and
output to a build area.

5) compile normal user native (C/C++) code. The code at this stage can
reference and rely on any wrapper functions and generated header files.

I use this process in practice with my projects and it works rather well.
Steps #3 and #4 can easily be incorporated into any build process and
greatly simplify java method calls from native user space.

Also note that the wrapper functions can be omitted completely (with an
option to 'javani' task), and the user can rely on entirely on the cached
JNI method and field IDs and invoke JNI API directly using those IDs. The
IDs are exported globally (declared in header files). Once initialized, the
wrapper functions are simply a convenience. The method and field IDs can be
utilized directly by the user if preferred over additional overhead of
wrapper functions.

There are some limitations. Unlike 'javah' task, my code does not parse the
binary classfile looking for constants. The task uses java reflection to
inspect classes, methods and fields. Therefore any constants (static final
fields) are typically optimized away by the compiler. These constants
however do exist in the 'javah' generated stub header files, which can be
incorporated into a build process. The 2 tasks are complimentatory to each
other. This is one area, where possibly classfile parser used by 'javah'
could be reused, but I haven't looked into this possibility yet.

Cheers,
mark...

"A government big enough to give you everything you want, is strong enough
to take everything you have."

- Thomas Jefferson



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


Mime
View raw message