From 47a9ceb931dc5db91eb3ce1733dfdabfa8ba9f99 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 14 Aug 2012 03:42:43 +0200 Subject: [PATCH] first commit. Just use "make tests" --- .gitignore | 7 + Makefile | 8 + README.md | 6 + setup.py | 71 +++ src/config.pxi | 1 + src/jni.pxi | 398 ++++++++++++ src/jnius.pyx | 1033 +++++++++++++++++++++++++++++++ src/jnius_jvm_android.pxi | 5 + src/jnius_jvm_desktop.pxi | 28 + tests/org/jnius/HelloWorld.java | 7 + tests/test_simple.py | 10 + 11 files changed, 1574 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 setup.py create mode 100644 src/config.pxi create mode 100644 src/jni.pxi create mode 100644 src/jnius.pyx create mode 100644 src/jnius_jvm_android.pxi create mode 100644 src/jnius_jvm_desktop.pxi create mode 100644 tests/org/jnius/HelloWorld.java create mode 100644 tests/test_simple.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..296c222 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyo +*.pyc +src/*.c +build +jnius.so +.*.swp +*.class diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c93c8cd --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY: build_ext tests + +build_ext: + python setup.py build_ext --inplace -f + +tests: build_ext + cd tests && javac org/jnius/HelloWorld.java + cd tests && env PYTHONPATH=..:$(PYTHONPATH) python test_simple.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..34b2cbd --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +PyJNIus +======= + +Python module to access Java class as Python class, using JNI. + +(Work in progress.) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1097d14 --- /dev/null +++ b/setup.py @@ -0,0 +1,71 @@ +from distutils.core import setup, Extension +from os import environ +from os.path import dirname, join +import sys + +libraries = [] +library_dirs = [] +extra_link_args = [] +includes_dirs = [] + +# detect Python for android +platform = sys.platform +ndkplatform = environ.get('NDKPLATFORM') +if ndkplatform is not None and environ.get('LIBLINK'): + platform = 'android' + +# detect cython +try: + from Cython.Distutils import build_ext + have_cython = True + ext = 'pyx' +except ImportError: + from distutils.command.build_ext import build_ext + have_cython = False + ext = 'c' + +if platform == 'android': + # for android, we use SDL... + libraries = ['sdl', 'log'] + library_dirs = ['libs/' + environ['ARCH']] +else: + import subprocess + # otherwise, we need to search the JDK_HOME + jdk_home = environ.get('JDK_HOME') + if not jdk_home: + jdk_home = subprocess.Popen('readlink -f /usr/bin/javac | sed "s:bin/javac::"', + shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if not jdk_home: + raise Exception('Unable to determine JDK_HOME') + + jre_home = environ.get('JRE_HOME') + if not jre_home: + jre_home = subprocess.Popen('readlink -f /usr/bin/java | sed "s:bin/java::"', + shell=True, stdout=subprocess.PIPE).communicate()[0].strip() + if not jre_home: + raise Exception('Unable to determine JRE_HOME') + cpu = 'i386' if sys.maxint == 2147483647 else 'amd64' + include_dirs = [ + join(jdk_home, 'include'), + join(jdk_home, 'include', 'linux')] + library_dirs = [join(jre_home, 'lib', cpu, 'server')] + extra_link_args = ['-Wl,-rpath', library_dirs[0]] + libraries = ['jvm'] + +# generate the config.pxi +with open(join(dirname(__file__), 'src', 'config.pxi'), 'w') as fd: + fd.write('DEF JNIUS_PLATFORM = {0!r}'.format(platform)) + +# create the extension +setup(name='jnius', + version='1.0', + cmdclass={'build_ext': build_ext}, + ext_modules=[ + Extension( + 'jnius', ['src/jnius.' + ext], + libraries=libraries, + library_dirs=library_dirs, + include_dirs=include_dirs, + extra_link_args=extra_link_args) + ] + ) diff --git a/src/config.pxi b/src/config.pxi new file mode 100644 index 0000000..e4f13c0 --- /dev/null +++ b/src/config.pxi @@ -0,0 +1 @@ +DEF JNIUS_PLATFORM = 'linux2' \ No newline at end of file diff --git a/src/jni.pxi b/src/jni.pxi new file mode 100644 index 0000000..6c71c00 --- /dev/null +++ b/src/jni.pxi @@ -0,0 +1,398 @@ +cdef extern from "jni.h": + + ctypedef unsigned char jboolean + ctypedef signed char jbyte + ctypedef unsigned short jchar + ctypedef short jshort + ctypedef int jint + ctypedef long long jlong + ctypedef float jfloat + ctypedef double jdouble + ctypedef void* jobject + + ctypedef jobject jclass + ctypedef jobject jstring + ctypedef jobject jarray + ctypedef jarray jobjectArray + ctypedef jarray jbooleanArray + ctypedef jarray jbyteArray + ctypedef jarray jcharArray + ctypedef jarray jshortArray + ctypedef jarray jintArray + ctypedef jarray jlongArray + ctypedef jarray jfloatArray + ctypedef jarray jdoubleArray + ctypedef jobject jthrowable + ctypedef jobject jweak + ctypedef jint jsize + + ctypedef char const_char "const_char" + ctypedef jchar const_jchar "const jchar" + ctypedef jbyte const_jbyte "const jbyte" + ctypedef jbyte const_jint "const jint" + ctypedef jboolean const_jboolean "const jboolean" + ctypedef jshort const_jshort "const jshort" + ctypedef jlong const_jlong "const jlong" + ctypedef jfloat const_jfloat "const jfloat" + ctypedef jdouble const_jdouble "const jdouble" + + ctypedef struct JNINativeMethod: + const_char* name + const_char* signature + void* fnPtr + + ctypedef union jvalue: + jboolean z + jbyte b + jchar c + jshort s + jint i + jlong j + jfloat f + jdouble d + jobject l + + ctypedef enum jobjectRefType: + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 + + + # some opaque definitions + ctypedef void *jmethodID + ctypedef void *jfieldID + + ctypedef struct JNINativeInterface + + ctypedef JNINativeInterface* JNIEnv + + ctypedef struct JNINativeInterface: + jint *GetVersion(JNIEnv *) + jclass (*DefineClass)(JNIEnv*, const_char*, jobject, const_jbyte*, + jsize) + jclass (*FindClass)(JNIEnv*, char*) + + jmethodID (*FromReflectedMethod)(JNIEnv*, jobject) + jfieldID (*FromReflectedField)(JNIEnv*, jobject) + # spec doesn't show jboolean parameter + jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean) + + jclass (*GetSuperclass)(JNIEnv*, jclass) + jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass) + + # spec doesn't show jboolean parameter + jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean) + + jint (*Throw)(JNIEnv*, jthrowable) + jint (*ThrowNew)(JNIEnv *, jclass, const_char *) + jthrowable (*ExceptionOccurred)(JNIEnv*) + void (*ExceptionDescribe)(JNIEnv*) + void (*ExceptionClear)(JNIEnv*) + void (*FatalError)(JNIEnv*, const_char*) + + jint (*PushLocalFrame)(JNIEnv*, jint) + jobject (*PopLocalFrame)(JNIEnv*, jobject) + + jobject (*NewGlobalRef)(JNIEnv*, jobject) + void (*DeleteGlobalRef)(JNIEnv*, jobject) + void (*DeleteLocalRef)(JNIEnv*, jobject) + jboolean (*IsSameObject)(JNIEnv*, jobject, jobject) + + jobject (*NewLocalRef)(JNIEnv*, jobject) + jint (*EnsureLocalCapacity)(JNIEnv*, jint) + + jobject (*AllocObject)(JNIEnv*, jclass) + jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...) + jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list) + jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*) + + jclass (*GetObjectClass)(JNIEnv*, jobject) + jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass) + jmethodID (*GetMethodID)(JNIEnv*, jclass, const_char*, const_char*) + + jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...) + jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...) + jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...) + jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...) + jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...) + jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...) + jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...) + jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) + jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) + jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) + jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...) + void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list) + void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) + + jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass, + jmethodID, ...) + void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass, + jmethodID, va_list) + void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass, + jmethodID, jvalue*) + + jfieldID (*GetFieldID)(JNIEnv*, jclass, const_char*, const_char*) + + jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID) + jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID) + jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID) + jchar (*GetCharField)(JNIEnv*, jobject, jfieldID) + jshort (*GetShortField)(JNIEnv*, jobject, jfieldID) + jint (*GetIntField)(JNIEnv*, jobject, jfieldID) + jlong (*GetLongField)(JNIEnv*, jobject, jfieldID) + jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID) + jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID) + + void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject) + void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean) + void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte) + void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar) + void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort) + void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint) + void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong) + void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat) + void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble) + + jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const_char*, const_char*) + + jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...) + jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...) + jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID, + va_list) + jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, + jvalue*) + jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...) + jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...) + jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...) + jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...) + jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...) + jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...) + jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...) + jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list) + jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...) + void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list) + void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) + + jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const_char*, + const_char*) + + jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID) + jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID) + jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID) + jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID) + jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID) + jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID) + jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID) + jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID) + jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID) + + void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject) + void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean) + void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte) + void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar) + void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort) + void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint) + void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong) + void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat) + void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble) + + jstring (*NewString)(JNIEnv*, const_jchar*, jsize) + jsize (*GetStringLength)(JNIEnv*, jstring) + const_jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*) + void (*ReleaseStringChars)(JNIEnv*, jstring, const_jchar*) + jstring (*NewStringUTF)(JNIEnv*, char*) + jsize (*GetStringUTFLength)(JNIEnv*, jstring) + # JNI spec says this returns const_jbyte*, but that's inconsistent + const_char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*) + void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const_char*) + jsize (*GetArrayLength)(JNIEnv*, jarray) + jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject) + jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize) + void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject) + + jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize) + jbyteArray (*NewByteArray)(JNIEnv*, jsize) + jcharArray (*NewCharArray)(JNIEnv*, jsize) + jshortArray (*NewShortArray)(JNIEnv*, jsize) + jintArray (*NewIntArray)(JNIEnv*, jsize) + jlongArray (*NewLongArray)(JNIEnv*, jsize) + jfloatArray (*NewFloatArray)(JNIEnv*, jsize) + jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize) + + jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*) + jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*) + jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*) + jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*) + jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*) + jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*) + jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*) + jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*) + + void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray, + jboolean*, jint) + void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, + jbyte*, jint) + void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray, + jchar*, jint) + void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray, + jshort*, jint) + void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, + jint*, jint) + void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray, + jlong*, jint) + void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray, + jfloat*, jint) + void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray, + jdouble*, jint) + + void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray, + jsize, jsize, jboolean*) + void (*GetByteArrayRegion)(JNIEnv*, jbyteArray, + jsize, jsize, jbyte*) + void (*GetCharArrayRegion)(JNIEnv*, jcharArray, + jsize, jsize, jchar*) + void (*GetShortArrayRegion)(JNIEnv*, jshortArray, + jsize, jsize, jshort*) + void (*GetIntArrayRegion)(JNIEnv*, jintArray, + jsize, jsize, jint*) + void (*GetLongArrayRegion)(JNIEnv*, jlongArray, + jsize, jsize, jlong*) + void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray, + jsize, jsize, jfloat*) + void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray, + jsize, jsize, jdouble*) + + # spec shows these without const some jni.h do, some don't + void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray, + jsize, jsize, const_jboolean*) + void (*SetByteArrayRegion)(JNIEnv*, jbyteArray, + jsize, jsize, const_jbyte*) + void (*SetCharArrayRegion)(JNIEnv*, jcharArray, + jsize, jsize, const_jchar*) + void (*SetShortArrayRegion)(JNIEnv*, jshortArray, + jsize, jsize, const_jshort*) + void (*SetIntArrayRegion)(JNIEnv*, jintArray, + jsize, jsize, const_jint*) + void (*SetLongArrayRegion)(JNIEnv*, jlongArray, + jsize, jsize, const_jlong*) + void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray, + jsize, jsize, const_jfloat*) + void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray, + jsize, jsize, const_jdouble*) + + #XXX not working with cython? + #jint (*RegisterNatives)(JNIEnv*, jclass, const_JNINativeMethod*, jint) + jint (*UnregisterNatives)(JNIEnv*, jclass) + jint (*MonitorEnter)(JNIEnv*, jobject) + jint (*MonitorExit)(JNIEnv*, jobject) + jint (*GetJavaVM)(JNIEnv*, JavaVM**) + + void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*) + void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*) + + void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*) + void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint) + + const_jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*) + void (*ReleaseStringCritical)(JNIEnv*, jstring, const_jchar*) + + jweak (*NewWeakGlobalRef)(JNIEnv*, jobject) + void (*DeleteWeakGlobalRef)(JNIEnv*, jweak) + + jboolean (*ExceptionCheck)(JNIEnv*) + + jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong) + void* (*GetDirectBufferAddress)(JNIEnv*, jobject) + jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject) + + jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject) + + ctypedef struct JavaVM: + pass diff --git a/src/jnius.pyx b/src/jnius.pyx new file mode 100644 index 0000000..0c29c71 --- /dev/null +++ b/src/jnius.pyx @@ -0,0 +1,1033 @@ + +''' +Java wrapper +============ + +With this module, you can create Python class that reflect a Java class, and use +it directly in Python. + +Example with static method +-------------------------- + +Java:: + + package org.test; + public class Hardware { + static int getDPI() { + return metrics.densityDpi; + } + } + +Python:: + + class Hardware(JavaClass): + __metaclass__ = MetaJavaClass + __javaclass__ = 'org/test/Hardware' + getDPI = JavaStaticMethod('()I') + + Hardware.getDPI() + + +Example with instance method +---------------------------- + +Java:: + + package org.test; + public class Action { + public String getName() { + return new String("Hello world") + } + } + +Python:: + + class Action(JavaClass): + __metaclass__ = MetaJavaClass + __javaclass__ = 'org/test/Action' + getName = JavaMethod('()Ljava/lang/String;') + + action = Action() + print action.getName() + # will output Hello World + + +Example with static/instance field +---------------------------------- + +Java:: + + package org.test; + public class Test { + public static String field1 = new String("hello"); + public String field2; + + public Test() { + this.field2 = new String("world"); + } + } + +Python:: + + class Test(JavaClass): + __metaclass__ = MetaJavaClass + __javaclass__ = 'org/test/Test' + + field1 = JavaStaticField('Ljava/lang/String;') + field2 = JavaField('Ljava/lang/String;') + + # access directly to the static field + print Test.field1 + + # create the instance, and access to the instance field + test = Test() + print test.field2 + +''' + +__all__ = ('JavaObject', 'JavaClass', 'JavaMethod', 'JavaStaticMethod', + 'JavaField', 'JavaStaticField', 'MetaJavaClass') + +from libc.stdlib cimport malloc, free + +include "jni.pxi" +include "config.pxi" +IF JNIUS_PLATFORM == "android": + include "jnius_jvm_android.pxi" +ELSE: + include "jnius_jvm_desktop.pxi" + + +cdef parse_definition(definition): + # not a function, just a field + if definition[0] != '(': + return definition, None + + # it's a function! + argdef, ret = definition[1:].split(')') + args = [] + + while len(argdef): + c = argdef[0] + + # read the array char + prefix = '' + if c == '[': + prefix = c + argdef = argdef[1:] + c = argdef[0] + + # native type + if c in 'ZBCSIJFD': + args.append(prefix + c) + argdef = argdef[1:] + continue + + # java class + if c == 'L': + c, argdef = argdef.split(';', 1) + args.append(prefix + c + ';') + + return ret, args + +cdef convert_jarray_to_python(JNIEnv *j_env, definition, jobject j_object): + cdef jboolean iscopy + cdef jboolean *j_booleans + cdef jbyte *j_bytes + cdef jchar *j_chars + cdef jshort *j_shorts + cdef jint *j_ints + cdef jlong *j_longs + cdef jfloat *j_floats + cdef jdouble *j_doubles + cdef object ret = None + cdef jsize array_size + + cdef int i + cdef jobject obj + cdef char *c_str + cdef bytes py_str + cdef JavaObject ret_jobject + + if j_object == NULL: + return None + + array_size = j_env[0].GetArrayLength(j_env, j_object) + + r = definition[0] + if r == 'Z': + j_booleans = j_env[0].GetBooleanArrayElements( + j_env, j_object, &iscopy) + ret = [(True if j_booleans[i] else False) + for i in range(array_size)] + if iscopy: + j_env[0].ReleaseBooleanArrayElements( + j_env, j_object, j_booleans, 0) + + elif r == 'B': + j_bytes = j_env[0].GetByteArrayElements( + j_env, j_object, &iscopy) + ret = [(j_bytes[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseByteArrayElements( + j_env, j_object, j_bytes, 0) + + elif r == 'C': + j_chars = j_env[0].GetCharArrayElements( + j_env, j_object, &iscopy) + ret = [(j_chars[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseCharArrayElements( + j_env, j_object, j_chars, 0) + + elif r == 'S': + j_shorts = j_env[0].GetShortArrayElements( + j_env, j_object, &iscopy) + ret = [(j_shorts[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseShortArrayElements( + j_env, j_object, j_shorts, 0) + + elif r == 'I': + j_ints = j_env[0].GetIntArrayElements( + j_env, j_object, &iscopy) + ret = [(j_ints[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseIntArrayElements( + j_env, j_object, j_ints, 0) + + elif r == 'J': + j_longs = j_env[0].GetLongArrayElements( + j_env, j_object, &iscopy) + ret = [(j_longs[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseLongArrayElements( + j_env, j_object, j_longs, 0) + + elif r == 'F': + j_floats = j_env[0].GetFloatArrayElements( + j_env, j_object, &iscopy) + ret = [(j_floats[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseFloatArrayElements( + j_env, j_object, j_floats, 0) + + elif r == 'D': + j_doubles = j_env[0].GetDoubleArrayElements( + j_env, j_object, &iscopy) + ret = [(j_doubles[i]) for i in range(array_size)] + if iscopy: + j_env[0].ReleaseDoubleArrayElements( + j_env, j_object, j_doubles, 0) + + elif r == 'L': + ret = [] + if definition == 'Ljava/lang/String;': + for i in range(array_size): + obj = j_env[0].GetObjectArrayElement( + j_env, j_object, i) + if obj == NULL: + ret.append(None) + continue + c_str = j_env[0].GetStringUTFChars( + j_env, obj, NULL) + py_str = c_str + j_env[0].ReleaseStringUTFChars( + j_env, j_object, c_str) + ret.append(py_str) + else: + for i in range(array_size): + obj = j_env[0].GetObjectArrayElement( + j_env, j_object, i) + if obj == NULL: + ret.append(None) + continue + ret_jobject = JavaObject() + ret_jobject.obj = obj + ret.append(ret_jobject) + else: + raise JavaException('Invalid return definition for array') + + return ret + + +cdef void populate_args(JNIEnv *j_env, list definition_args, jvalue *j_args, args): + # do the conversion from a Python object to Java from a Java definition + cdef JavaObject jo + cdef JavaClass jc + cdef int index + for index, argtype in enumerate(definition_args): + py_arg = args[index] + if argtype == 'Z': + j_args[index].z = py_arg + elif argtype == 'B': + j_args[index].b = py_arg + elif argtype == 'C': + j_args[index].c = ord(py_arg) + elif argtype == 'S': + j_args[index].s = py_arg + elif argtype == 'I': + j_args[index].i = py_arg + elif argtype == 'J': + j_args[index].j = py_arg + elif argtype == 'F': + j_args[index].f = py_arg + elif argtype == 'D': + j_args[index].d = py_arg + elif argtype[0] == 'L': + if py_arg is None: + j_args[index].l = NULL + elif isinstance(py_arg, basestring) and \ + argtype == 'Ljava/lang/String;': + j_args[index].l = j_env[0].NewStringUTF( + j_env, py_arg) + elif isinstance(py_arg, JavaClass): + jc = py_arg + if jc.__javaclass__ != argtype[1:-1]: + raise JavaException('Invalid class argument, want ' + '{0!r}, got {1!r}'.format( + argtype[1:-1], jc.__javaclass__)) + j_args[index].l = jc.j_self + elif isinstance(py_arg, JavaObject): + jo = py_arg + j_args[index].l = jo.obj + raise JavaException('JavaObject needed for argument ' + '{0}'.format(index)) + else: + raise JavaException('Invalid python object for this ' + 'argument. Want {0!r}, got {1!r}'.format( + argtype[1:-1], py_arg)) + elif argtype[0] == '[': + if not isinstance(py_arg, list) and \ + not isinstance(py_arg, tuple): + raise JavaException('Expecting a python list/tuple, got ' + '{0!r}'.format(py_arg)) + + j_args[index].l = convert_pyarray_to_java( + j_env, argtype[1:], py_arg) + +cdef jobject convert_pyarray_to_java(JNIEnv *j_env, definition, pyarray): + cdef jobject ret = NULL + cdef int array_size = len(pyarray) + cdef int i + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef jstring j_string + cdef jclass j_class + cdef JavaObject jo + cdef JavaClass jc + + if definition == 'Z': + ret = j_env[0].NewBooleanArray(j_env, array_size) + for i in range(array_size): + j_boolean = 1 if pyarray[i] else 0 + j_env[0].SetBooleanArrayRegion(j_env, + ret, i, 1, &j_boolean) + + elif definition == 'B': + ret = j_env[0].NewByteArray(j_env, array_size) + for i in range(array_size): + j_byte = pyarray[i] + j_env[0].SetByteArrayRegion(j_env, + ret, i, 1, &j_byte) + + elif definition == 'C': + ret = j_env[0].NewCharArray(j_env, array_size) + for i in range(array_size): + j_char = ord(pyarray[i]) + j_env[0].SetCharArrayRegion(j_env, + ret, i, 1, &j_char) + + elif definition == 'S': + ret = j_env[0].NewShortArray(j_env, array_size) + for i in range(array_size): + j_short = pyarray[i] + j_env[0].SetShortArrayRegion(j_env, + ret, i, 1, &j_short) + + elif definition == 'I': + ret = j_env[0].NewIntArray(j_env, array_size) + for i in range(array_size): + j_int = pyarray[i] + j_env[0].SetIntArrayRegion(j_env, + ret, i, 1, &j_int) + + elif definition == 'J': + ret = j_env[0].NewLongArray(j_env, array_size) + for i in range(array_size): + j_long = pyarray[i] + j_env[0].SetLongArrayRegion(j_env, + ret, i, 1, &j_long) + + elif definition == 'F': + ret = j_env[0].NewFloatArray(j_env, array_size) + for i in range(array_size): + j_float = pyarray[i] + j_env[0].SetFloatArrayRegion(j_env, + ret, i, 1, &j_float) + + elif definition == 'D': + ret = j_env[0].NewDoubleArray(j_env, array_size) + for i in range(array_size): + j_double = pyarray[i] + j_env[0].SetDoubleArrayRegion(j_env, + ret, i, 1, &j_double) + + elif definition[0] == 'L': + j_class = j_env[0].FindClass( + j_env, definition[1:-1]) + if j_class == NULL: + raise JavaException('Cannot create array with a class not ' + 'found {0!r}'.format(definition[1:-1])) + ret = j_env[0].NewObjectArray( + j_env, array_size, j_class, NULL) + for i in range(array_size): + arg = pyarray[i] + if arg is None: + j_env[0].SetObjectArrayElement( + j_env, ret, i, NULL) + elif isinstance(arg, basestring) and \ + definition == 'Ljava/lang/String;': + j_string = j_env[0].NewStringUTF( + j_env, arg) + j_env[0].SetObjectArrayElement( + j_env, ret, i, j_string) + elif isinstance(arg, JavaClass): + jc = arg + if jc.__javaclass__ != definition[1:-1]: + raise JavaException('Invalid class argument, want ' + '{0!r}, got {1!r}'.format( + definition[1:-1], + jc.__javaclass__)) + j_env[0].SetObjectArrayElement( + j_env, ret, i, jc.j_self) + elif isinstance(arg, JavaObject): + jo = arg + j_env[0].SetObjectArrayElement( + j_env, ret, i, jo.obj) + else: + raise JavaException('Invalid variable used for L array') + + else: + raise JavaException('Invalid array definition') + + return ret + + +class JavaException(Exception): + '''Can be a real java exception, or just an exception from the wrapper. + ''' + pass + + +cdef class JavaObject(object): + '''Can contain any Java object. Used to store instance, or whatever. + ''' + + cdef jobject obj + + def __cinit__(self): + self.obj = NULL + + +cdef class JavaClassStorage: + cdef JNIEnv *j_env + cdef jclass j_cls + + def __cinit__(self): + self.j_env = NULL + self.j_cls = NULL + + +class MetaJavaClass(type): + def __new__(meta, classname, bases, classDict): + meta.resolve_class(classDict) + return type.__new__(meta, classname, bases, classDict) + + @classmethod + def resolve_class(meta, classDict): + # search the Java class, and bind to our object + if not '__javaclass__' in classDict: + raise JavaException('__javaclass__ definition missing') + + cdef JavaClassStorage jcs = JavaClassStorage() + cdef str __javaclass__ = classDict['__javaclass__'] + + jcs.j_env = get_jnienv() + if jcs.j_env == NULL: + raise JavaException('Unable to get the Android JNI Environment') + + jcs.j_cls = jcs.j_env[0].FindClass(jcs.j_env, + __javaclass__) + if jcs.j_cls == NULL: + raise JavaException('Unable to found the class' + ' {0}'.format(__javaclass__)) + + classDict['__cls_storage'] = jcs + + # search all the static JavaMethod within our class, and resolve them + cdef JavaMethod jm + for name, value in classDict.iteritems(): + if not isinstance(value, JavaMethod): + continue + jm = value + if not jm.is_static: + continue + jm.resolve_static_method(jcs.j_env, jcs.j_cls, name) + + # search all the static JavaField within our class, and resolve them + cdef JavaField jf + for name, value in classDict.iteritems(): + if not isinstance(value, JavaField): + continue + jf = value + if not jf.is_static: + continue + jf.resolve_static_field(jcs.j_env, jcs.j_cls, name) + + +cdef class JavaClass(object): + '''Main class to do introspection. + ''' + + cdef JNIEnv *j_env + cdef jclass j_cls + cdef jobject j_self + + def __cinit__(self, *args): + self.j_env = NULL + self.j_cls = NULL + self.j_self = NULL + + def __init__(self, *args): + super(JavaClass, self).__init__() + # copy the current attribute in the storage to our class + cdef JavaClassStorage jcs = self.__cls_storage + self.j_env = jcs.j_env + self.j_cls = jcs.j_cls + + self.call_constructor(args) + self.resolve_methods() + self.resolve_fields() + + cdef void call_constructor(self, args): + # the goal is to found the class constructor, and call it with the + # correct arguments. + cdef jvalue *j_args = NULL + cdef jmethodID constructor = NULL + + # get the constructor definition if exist + definition = '()V' + if hasattr(self, '__javaconstructor__'): + definition = self.__javaconstructor__ + self.definition = definition + d_ret, d_args = parse_definition(definition) + if len(args) != len(d_args): + raise JavaException('Invalid call, number of argument' + ' mismatch for constructor') + + try: + # convert python arguments to java arguments + if len(args): + j_args = malloc(sizeof(jvalue) * len(d_args)) + if j_args == NULL: + raise MemoryError('Unable to allocate memory for java args') + populate_args(self.j_env, d_args, j_args, args) + + # get the java constructor + constructor = self.j_env[0].GetMethodID( + self.j_env, self.j_cls, '', definition) + if constructor == NULL: + raise JavaException('Unable to found the constructor' + ' for {0}'.format(self.__javaclass__)) + + # create the object + self.j_self = self.j_env[0].NewObjectA(self.j_env, self.j_cls, + constructor, j_args) + if self.j_self == NULL: + raise JavaException('Unable to instanciate {0}'.format( + self.__javaclass__)) + + finally: + if j_args != NULL: + free(j_args) + + cdef void resolve_methods(self): + # search all the JavaMethod within our class, and resolve them + cdef JavaMethod jm + for name, value in self.__class__.__dict__.iteritems(): + if not isinstance(value, JavaMethod): + continue + jm = value + if jm.is_static: + continue + jm.resolve_method(self, name) + + cdef void resolve_fields(self): + # search all the JavaField within our class, and resolve them + cdef JavaField jf + for name, value in self.__class__.__dict__.iteritems(): + if not isinstance(value, JavaField): + continue + jf = value + if jf.is_static: + continue + jf.resolve_field(self, name) + + +cdef class JavaField(object): + cdef jfieldID j_field + cdef JNIEnv *j_env + cdef jclass j_cls + cdef jobject j_self + cdef bytes definition + cdef object is_static + + def __cinit__(self, definition, **kwargs): + self.j_field = NULL + self.j_env = NULL + self.j_cls = NULL + self.j_self = NULL + + def __init__(self, definition, **kwargs): + super(JavaField, self).__init__() + self.definition = definition + self.is_static = kwargs.get('static', False) + + cdef void resolve_field(self, JavaClass jc, bytes name): + # called by JavaClass when we want to resolve the field name + assert(self.is_static is False) + self.j_env = jc.j_env + self.j_cls = jc.j_cls + self.j_self = jc.j_self + self.j_field = self.j_env[0].GetFieldID( + self.j_env, self.j_cls, name, + self.definition) + + if self.j_field == NULL: + raise JavaException('Unable to found the field' + ' {0} in {1}'.format(name, jc.__javaclass__)) + + cdef void resolve_static_field(self, JNIEnv *j_env, jclass j_cls, bytes name): + # called by JavaClass when we want to resolve the field name + assert(self.is_static is True) + self.j_env = j_env + self.j_cls = j_cls + self.j_field = self.j_env[0].GetStaticFieldID( + self.j_env, self.j_cls, name, + self.definition) + + if self.j_field == NULL: + raise JavaException('Unable to found the field' + ' {0}'.format(name)) + + def __get__(self, obj, objtype): + if obj is None: + return self.read_static_field() + return self.read_field() + + cdef read_field(self): + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef jobject j_object + cdef char *c_str + cdef bytes py_str + cdef object ret = None + cdef JavaObject ret_jobject + + # return type of the java method + r = self.definition[0] + + # now call the java method + if r == 'Z': + j_boolean = self.j_env[0].GetBooleanField( + self.j_env, self.j_self, self.j_field) + ret = True if j_boolean else False + elif r == 'B': + j_byte = self.j_env[0].GetByteField( + self.j_env, self.j_self, self.j_field) + ret = j_byte + elif r == 'C': + j_char = self.j_env[0].GetCharField( + self.j_env, self.j_self, self.j_field) + ret = chr(j_char) + elif r == 'S': + j_short = self.j_env[0].GetShortField( + self.j_env, self.j_self, self.j_field) + ret = j_short + elif r == 'I': + j_int = self.j_env[0].GetIntField( + self.j_env, self.j_self, self.j_field) + ret = j_int + elif r == 'J': + j_long = self.j_env[0].GetLongField( + self.j_env, self.j_self, self.j_field) + ret = j_long + elif r == 'F': + j_float = self.j_env[0].GetFloatField( + self.j_env, self.j_self, self.j_field) + ret = j_float + elif r == 'D': + j_double = self.j_env[0].GetDoubleField( + self.j_env, self.j_self, self.j_field) + ret = j_double + elif r == 'L': + j_object = self.j_env[0].GetObjectField( + self.j_env, self.j_self, self.j_field) + if j_object == NULL: + return None + if self.definition == 'Ljava/lang/String;': + c_str = self.j_env[0].GetStringUTFChars( + self.j_env, j_object, NULL) + py_str = c_str + self.j_env[0].ReleaseStringUTFChars( + self.j_env, j_object, c_str) + ret = py_str + else: + ret_jobject = JavaObject() + ret_jobject.obj = j_object + ret = ret_jobject + elif r == '[': + r = self.definition[1:] + j_object = self.j_env[0].GetObjectField( + self.j_env, self.j_self, self.j_field) + ret = convert_jarray_to_python(self.j_env, r, j_object) + else: + raise Exception('Invalid field definition') + + return ret + + cdef read_static_field(self): + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef jobject j_object + cdef char *c_str + cdef bytes py_str + cdef object ret = None + cdef JavaObject ret_jobject + + # return type of the java method + r = self.definition[0] + + # now call the java method + if r == 'Z': + j_boolean = self.j_env[0].GetStaticBooleanField( + self.j_env, self.j_self, self.j_field) + ret = True if j_boolean else False + elif r == 'B': + j_byte = self.j_env[0].GetStaticByteField( + self.j_env, self.j_self, self.j_field) + ret = j_byte + elif r == 'C': + j_char = self.j_env[0].GetStaticCharField( + self.j_env, self.j_self, self.j_field) + ret = chr(j_char) + elif r == 'S': + j_short = self.j_env[0].GetStaticShortField( + self.j_env, self.j_self, self.j_field) + ret = j_short + elif r == 'I': + j_int = self.j_env[0].GetStaticIntField( + self.j_env, self.j_self, self.j_field) + ret = j_int + elif r == 'J': + j_long = self.j_env[0].GetStaticLongField( + self.j_env, self.j_self, self.j_field) + ret = j_long + elif r == 'F': + j_float = self.j_env[0].GetStaticFloatField( + self.j_env, self.j_self, self.j_field) + ret = j_float + elif r == 'D': + j_double = self.j_env[0].GetStaticDoubleField( + self.j_env, self.j_self, self.j_field) + ret = j_double + elif r == 'L': + j_object = self.j_env[0].GetStaticObjectField( + self.j_env, self.j_self, self.j_field) + if j_object == NULL: + return None + if self.definition == 'Ljava/lang/String;': + c_str = self.j_env[0].GetStringUTFChars( + self.j_env, j_object, NULL) + py_str = c_str + self.j_env[0].ReleaseStringUTFChars( + self.j_env, j_object, c_str) + ret = py_str + else: + ret_jobject = JavaObject() + ret_jobject.obj = j_object + ret = ret_jobject + elif r == '[': + r = self.definition[1:] + j_object = self.j_env[0].GetStaticObjectField( + self.j_env, self.j_self, self.j_field) + ret = convert_jarray_to_python(self.j_env, r, j_object) + else: + raise Exception('Invalid field definition') + + return ret + + +cdef class JavaMethod(object): + '''Used to resolve a Java method, and do the call + ''' + cdef jmethodID j_method + cdef JNIEnv *j_env + cdef jclass j_cls + cdef jobject j_self + cdef bytes definition + cdef object is_static + cdef object definition_return + cdef object definition_args + + def __cinit__(self, definition, **kwargs): + self.j_method = NULL + self.j_env = NULL + self.j_cls = NULL + self.j_self = NULL + + def __init__(self, definition, **kwargs): + super(JavaMethod, self).__init__() + self.definition = definition + self.definition_return, self.definition_args = \ + parse_definition(definition) + self.is_static = kwargs.get('static', False) + + cdef void resolve_method(self, JavaClass jc, bytes name): + # called by JavaClass when we want to resolve the method name + assert(self.is_static is False) + self.j_env = jc.j_env + self.j_cls = jc.j_cls + self.j_self = jc.j_self + self.j_method = self.j_env[0].GetMethodID( + self.j_env, self.j_cls, name, + self.definition) + + if self.j_method == NULL: + raise JavaException('Unable to found the method' + ' {0} in {1}'.format(name, jc.__javaclass__)) + + cdef void resolve_static_method(self, JNIEnv *j_env, jclass j_cls, bytes name): + # called by JavaClass when we want to resolve the method name + assert(self.is_static is True) + self.j_env = j_env + self.j_cls = j_cls + self.j_method = self.j_env[0].GetStaticMethodID( + self.j_env, self.j_cls, name, + self.definition) + + if self.j_method == NULL: + raise JavaException('Unable to found the method' + ' {0}'.format(name)) + + def __call__(self, *args): + # argument array to pass to the method + cdef jvalue *j_args = NULL + cdef list d_args = self.definition_args + if len(args) != len(d_args): + raise JavaException('Invalid call, number of argument mismatch') + + try: + # convert python argument if necessary + if len(args): + j_args = malloc(sizeof(jvalue) * len(d_args)) + if j_args == NULL: + raise MemoryError('Unable to allocate memory for java args') + populate_args(self.j_env, self.definition_args, j_args, args) + + # do the call + if self.is_static: + return self.call_staticmethod(j_args) + return self.call_method(j_args) + + finally: + if j_args != NULL: + free(j_args) + + cdef call_method(self, jvalue *j_args): + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef jobject j_object + cdef char *c_str + cdef bytes py_str + cdef object ret = None + cdef JavaObject ret_jobject + + # return type of the java method + r = self.definition_return[0] + + # now call the java method + if r == 'V': + self.j_env[0].CallVoidMethodA( + self.j_env, self.j_self, self.j_method, j_args) + elif r == 'Z': + j_boolean = self.j_env[0].CallBooleanMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = True if j_boolean else False + elif r == 'B': + j_byte = self.j_env[0].CallByteMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_byte + elif r == 'C': + j_char = self.j_env[0].CallCharMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = chr(j_char) + elif r == 'S': + j_short = self.j_env[0].CallShortMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_short + elif r == 'I': + j_int = self.j_env[0].CallIntMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_int + elif r == 'J': + j_long = self.j_env[0].CallLongMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_long + elif r == 'F': + j_float = self.j_env[0].CallFloatMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_float + elif r == 'D': + j_double = self.j_env[0].CallDoubleMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = j_double + elif r == 'L': + j_object = self.j_env[0].CallObjectMethodA( + self.j_env, self.j_self, self.j_method, j_args) + if j_object == NULL: + return None + if self.definition_return == 'Ljava/lang/String;': + c_str = self.j_env[0].GetStringUTFChars( + self.j_env, j_object, NULL) + py_str = c_str + self.j_env[0].ReleaseStringUTFChars( + self.j_env, j_object, c_str) + ret = py_str + else: + ret_jobject = JavaObject() + ret_jobject.obj = j_object + ret = ret_jobject + elif r == '[': + r = self.definition_return[1:] + j_object = self.j_env[0].CallObjectMethodA( + self.j_env, self.j_self, self.j_method, j_args) + ret = convert_jarray_to_python(self.j_env, r, j_object) + else: + raise Exception('Invalid return definition?') + + return ret + + cdef call_staticmethod(self, jvalue *j_args): + cdef jboolean j_boolean + cdef jbyte j_byte + cdef jchar j_char + cdef jshort j_short + cdef jint j_int + cdef jlong j_long + cdef jfloat j_float + cdef jdouble j_double + cdef jobject j_object + cdef char *c_str + cdef bytes py_str + cdef object ret = None + cdef JavaObject ret_jobject + + # return type of the java method + r = self.definition_return[0] + + # now call the java method + if r == 'V': + self.j_env[0].CallStaticVoidMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + elif r == 'Z': + j_boolean = self.j_env[0].CallStaticBooleanMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = True if j_boolean else False + elif r == 'B': + j_byte = self.j_env[0].CallStaticByteMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_byte + elif r == 'C': + j_char = self.j_env[0].CallStaticCharMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = chr(j_char) + elif r == 'S': + j_short = self.j_env[0].CallStaticShortMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_short + elif r == 'I': + j_int = self.j_env[0].CallStaticIntMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_int + elif r == 'J': + j_long = self.j_env[0].CallStaticLongMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_long + elif r == 'F': + j_float = self.j_env[0].CallStaticFloatMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_float + elif r == 'D': + j_double = self.j_env[0].CallStaticDoubleMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = j_double + elif r == 'L': + # accept only string for the moment + j_object = self.j_env[0].CallStaticObjectMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + if self.definition_return == 'Ljava/lang/String;': + c_str = self.j_env[0].GetStringUTFChars( + self.j_env, j_object, NULL) + py_str = c_str + self.j_env[0].ReleaseStringUTFChars( + self.j_env, j_object, c_str) + ret = py_str + else: + ret_jobject = JavaObject() + ret_jobject.obj = j_object + ret = ret_jobject + elif r == '[': + r = self.definition_return[1:] + j_object = self.j_env[0].CallStaticObjectMethodA( + self.j_env, self.j_cls, self.j_method, j_args) + ret = convert_jarray_to_python(self.j_env, r, j_object) + else: + raise Exception('Invalid return definition?') + + return ret + +class JavaStaticMethod(JavaMethod): + def __init__(self, definition, **kwargs): + kwargs['static'] = True + super(JavaStaticMethod, self).__init__(definition, **kwargs) + +class JavaStaticField(JavaField): + def __init__(self, definition, **kwargs): + kwargs['static'] = True + super(JavaStaticField, self).__init__(definition, **kwargs) diff --git a/src/jnius_jvm_android.pxi b/src/jnius_jvm_android.pxi new file mode 100644 index 0000000..169610a --- /dev/null +++ b/src/jnius_jvm_android.pxi @@ -0,0 +1,5 @@ +# on android, rely on SDL to get the JNI env +cdef extern JNIEnv *SDL_ANDROID_GetJNIEnv() + +cdef JNIEnv *get_jnienv(): + SDL_ANDROID_GetJNIEnv() diff --git a/src/jnius_jvm_desktop.pxi b/src/jnius_jvm_desktop.pxi new file mode 100644 index 0000000..9d30401 --- /dev/null +++ b/src/jnius_jvm_desktop.pxi @@ -0,0 +1,28 @@ +# on desktop, we need to create an env :) +# example taken from http://www.inonit.com/cygwin/jni/invocationApi/c.html + +cdef extern jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) +cdef extern from "jni.h": + int JNI_VERSION_1_4 + jboolean JNI_FALSE + ctypedef struct JavaVMInitArgs: + jint version + jint nOptions + jboolean ignoreUnrecognized + +cdef JNIEnv *default_env = NULL + +cdef void create_jnienv(): + cdef JavaVM* jvm + cdef JavaVMInitArgs args + + args.version = JNI_VERSION_1_4 + args.nOptions = 0 + args.ignoreUnrecognized = JNI_FALSE + + JNI_CreateJavaVM(&jvm, &default_env, &args) + +cdef JNIEnv *get_jnienv(): + if default_env == NULL: + create_jnienv() + return default_env diff --git a/tests/org/jnius/HelloWorld.java b/tests/org/jnius/HelloWorld.java new file mode 100644 index 0000000..d53c88e --- /dev/null +++ b/tests/org/jnius/HelloWorld.java @@ -0,0 +1,7 @@ +package org.jnius; + +public class HelloWorld { + public void hello() { + System.out.println("Print from java!"); + } +} diff --git a/tests/test_simple.py b/tests/test_simple.py new file mode 100644 index 0000000..9819489 --- /dev/null +++ b/tests/test_simple.py @@ -0,0 +1,10 @@ +from jnius import JavaClass, MetaJavaClass, JavaMethod + +class HelloWorld(JavaClass): + __metaclass__ = MetaJavaClass + __javaclass__ = 'org/jnius/HelloWorld' + + hello = JavaMethod('()V') + +a = HelloWorld() +a.hello()