diff --git a/jni/jni.go b/jni/jni.go index f497864..24f8ed4 100644 --- a/jni/jni.go +++ b/jni/jni.go @@ -11,6 +11,7 @@ import ( "fmt" "reflect" "runtime" + "sync" "unicode/utf16" "unsafe" ) @@ -113,10 +114,34 @@ static jbyteArray jni_NewByteArray(JNIEnv *env, jsize length) { return (*env)->NewByteArray(env, length); } +static jboolean *jni_GetBooleanArrayElements(JNIEnv *env, jbooleanArray arr) { + return (*env)->GetBooleanArrayElements(env, arr, NULL); +} + +static void jni_ReleaseBooleanArrayElements(JNIEnv *env, jbooleanArray arr, jboolean *elems, jint mode) { + (*env)->ReleaseBooleanArrayElements(env, arr, elems, mode); +} + static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { return (*env)->GetByteArrayElements(env, arr, NULL); } +static jint *jni_GetIntArrayElements(JNIEnv *env, jintArray arr) { + return (*env)->GetIntArrayElements(env, arr, NULL); +} + +static void jni_ReleaseIntArrayElements(JNIEnv *env, jintArray arr, jint *elems, jint mode) { + (*env)->ReleaseIntArrayElements(env, arr, elems, mode); +} + +static jlong *jni_GetLongArrayElements(JNIEnv *env, jlongArray arr) { + return (*env)->GetLongArrayElements(env, arr, NULL); +} + +static void jni_ReleaseLongArrayElements(JNIEnv *env, jlongArray arr, jlong *elems, jint mode) { + (*env)->ReleaseLongArrayElements(env, arr, elems, mode); +} + static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *elems, jint mode) { (*env)->ReleaseByteArrayElements(env, arr, elems, mode); } @@ -124,6 +149,18 @@ static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *ele static jsize jni_GetArrayLength(JNIEnv *env, jarray arr) { return (*env)->GetArrayLength(env, arr); } + +static void jni_DeleteLocalRef(JNIEnv *env, jobject localRef) { + return (*env)->DeleteLocalRef(env, localRef); +} + +static jobject jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index) { + return (*env)->GetObjectArrayElement(env, array, index); +} + +static jboolean jni_IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz) { + return (*env)->IsInstanceOf(env, obj, clazz); +} */ import "C" @@ -132,15 +169,27 @@ type JVM C.JavaVM type Env C.JNIEnv type ( - Class C.jclass - Object C.jobject - MethodID C.jmethodID - String C.jstring - ByteArray C.jbyteArray - Boolean C.jboolean - Value uint64 // All JNI types fit into 64-bits. + Class C.jclass + Object C.jobject + MethodID C.jmethodID + String C.jstring + ByteArray C.jbyteArray + ObjectArray C.jobjectArray + BooleanArray C.jbooleanArray + LongArray C.jlongArray + IntArray C.jintArray + Boolean C.jboolean + Value uint64 // All JNI types fit into 64-bits. ) +// Cached class handles. +var classes struct { + once sync.Once + stringClass, integerClass Class + + integerIntValue MethodID +} + func env(e *Env) *C.JNIEnv { return (*C.JNIEnv)(unsafe.Pointer(e)) } @@ -222,7 +271,7 @@ func CallIntMethod(e *Env, obj Object, method MethodID, args ...Value) (int32, e return int32(res), exception(e) } -// GetByteArrayElements returns the contents of the array. +// GetByteArrayElements returns the contents of the byte array. func GetByteArrayElements(e *Env, jarr ByteArray) []byte { if jarr == 0 { return nil @@ -236,6 +285,76 @@ func GetByteArrayElements(e *Env, jarr ByteArray) []byte { return s } +// GetBooleanArrayElements returns the contents of the boolean array. +func GetBooleanArrayElements(e *Env, jarr BooleanArray) []bool { + if jarr == 0 { + return nil + } + size := C.jni_GetArrayLength(env(e), C.jarray(jarr)) + elems := C.jni_GetBooleanArrayElements(env(e), C.jbooleanArray(jarr)) + defer C.jni_ReleaseBooleanArrayElements(env(e), C.jbooleanArray(jarr), elems, 0) + backing := (*(*[1 << 30]C.jboolean)(unsafe.Pointer(elems)))[:size:size] + r := make([]bool, len(backing)) + for i, b := range backing { + r[i] = b == C.JNI_TRUE + } + return r +} + +// GetStringArrayElements returns the contents of the String array. +func GetStringArrayElements(e *Env, jarr ObjectArray) []string { + var strings []string + iterateObjectArray(e, jarr, func(e *Env, idx int, item Object) { + s := GoString(e, String(item)) + strings = append(strings, s) + }) + return strings +} + +// GetIntArrayElements returns the contents of the int array. +func GetIntArrayElements(e *Env, jarr IntArray) []int { + if jarr == 0 { + return nil + } + size := C.jni_GetArrayLength(env(e), C.jarray(jarr)) + elems := C.jni_GetIntArrayElements(env(e), C.jintArray(jarr)) + defer C.jni_ReleaseIntArrayElements(env(e), C.jintArray(jarr), elems, 0) + backing := (*(*[1 << 27]C.jint)(unsafe.Pointer(elems)))[:size:size] + r := make([]int, len(backing)) + for i, l := range backing { + r[i] = int(l) + } + return r +} + +// GetLongArrayElements returns the contents of the long array. +func GetLongArrayElements(e *Env, jarr LongArray) []int64 { + if jarr == 0 { + return nil + } + size := C.jni_GetArrayLength(env(e), C.jarray(jarr)) + elems := C.jni_GetLongArrayElements(env(e), C.jlongArray(jarr)) + defer C.jni_ReleaseLongArrayElements(env(e), C.jlongArray(jarr), elems, 0) + backing := (*(*[1 << 27]C.jlong)(unsafe.Pointer(elems)))[:size:size] + r := make([]int64, len(backing)) + for i, l := range backing { + r[i] = int64(l) + } + return r +} + +func iterateObjectArray(e *Env, jarr ObjectArray, f func(e *Env, idx int, item Object)) { + if jarr == 0 { + return + } + size := C.jni_GetArrayLength(env(e), C.jarray(jarr)) + for i := 0; i < int(size); i++ { + item := C.jni_GetObjectArrayElement(env(e), C.jobjectArray(jarr), C.jint(i)) + f(e, i, Object(item)) + C.jni_DeleteLocalRef(env(e), item) + } +} + // NewByteArray allocates a Java byte array with the content. It // panics if the allocation fails. func NewByteArray(e *Env, content []byte) ByteArray {