263 lines
7.2 KiB
Plaintext
263 lines
7.2 KiB
Plaintext
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// C support functions for bindings. This file is copied into the
|
|
// generated gomobile_bind package and compiled along with the
|
|
// generated binding files.
|
|
|
|
#include <android/log.h>
|
|
#include <errno.h>
|
|
#include <jni.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include "seq.h"
|
|
#include "_cgo_export.h"
|
|
|
|
#define NULL_REFNUM 41
|
|
|
|
static JavaVM *jvm;
|
|
// jnienvs holds the per-thread JNIEnv* for Go threads where we called AttachCurrentThread.
|
|
// A pthread key destructor is supplied to call DetachCurrentThread on exit. This trick is
|
|
// documented in http://developer.android.com/training/articles/perf-jni.html under "Threads".
|
|
static pthread_key_t jnienvs;
|
|
|
|
static jclass seq_class;
|
|
static jmethodID seq_throw_exc;
|
|
static jmethodID seq_getRef;
|
|
static jmethodID seq_decRef;
|
|
static jmethodID seq_incRef;
|
|
|
|
static jmethodID throwable_getMessage;
|
|
|
|
static jfieldID ref_objField;
|
|
|
|
// env_destructor is registered as a thread data key destructor to
|
|
// clean up a Go thread that is attached to the JVM.
|
|
static void env_destructor(void *env) {
|
|
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) {
|
|
LOG_INFO("failed to detach current thread");
|
|
}
|
|
}
|
|
|
|
static JNIEnv *go_seq_get_thread_env(void) {
|
|
JNIEnv *env;
|
|
jint ret = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6);
|
|
if (ret != JNI_OK) {
|
|
if (ret != JNI_EDETACHED) {
|
|
LOG_FATAL("failed to get thread env");
|
|
return;
|
|
}
|
|
if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) {
|
|
LOG_FATAL("failed to attach current thread");
|
|
return;
|
|
}
|
|
pthread_setspecific(jnienvs, env);
|
|
}
|
|
return env;
|
|
}
|
|
|
|
void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg) {
|
|
if (msg != NULL && (*env)->GetStringLength(env, msg) > 0) {
|
|
(*env)->CallStaticVoidMethod(env, seq_class, seq_throw_exc, msg);
|
|
}
|
|
}
|
|
|
|
jstring go_seq_get_exception_message(JNIEnv *env) {
|
|
jthrowable exc = (*env)->ExceptionOccurred(env);
|
|
if (!exc) {
|
|
return NULL;
|
|
}
|
|
(*env)->ExceptionClear(env);
|
|
return (*env)->CallObjectMethod(env, exc, throwable_getMessage);
|
|
}
|
|
|
|
jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) {
|
|
if (s.ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
jbyteArray res = (*env)->NewByteArray(env, s.len);
|
|
if (res == NULL) {
|
|
LOG_FATAL("NewByteArray failed");
|
|
return res;
|
|
}
|
|
(*env)->SetByteArrayRegion(env, res, 0, s.len, s.ptr);
|
|
if (copy) {
|
|
free(s.ptr);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
nstring go_seq_from_java_string(JNIEnv *env, jstring str, int copy) {
|
|
struct nstring res = {NULL, 0};
|
|
if (str == NULL) {
|
|
return res;
|
|
}
|
|
jsize nchars = (*env)->GetStringLength(env, str);
|
|
jchar *chars = (jchar *)(*env)->GetStringChars(env, str, NULL);
|
|
if (chars == NULL) {
|
|
LOG_FATAL("GetStringChars failed");
|
|
return res;
|
|
}
|
|
if (copy) {
|
|
void *arr_copy = malloc(nchars*2);
|
|
if (arr_copy == NULL) {
|
|
LOG_FATAL("malloc failed");
|
|
return res;
|
|
}
|
|
memcpy(arr_copy, chars, nchars*2);
|
|
(*env)->ReleaseStringChars(env, str, chars);
|
|
chars = (jchar *)arr_copy;
|
|
}
|
|
res.chars = chars;
|
|
res.len = nchars;
|
|
return res;
|
|
}
|
|
|
|
nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) {
|
|
struct nbyteslice res = {NULL, 0};
|
|
if (arr == NULL) {
|
|
return res;
|
|
}
|
|
|
|
jsize len = (*env)->GetArrayLength(env, arr);
|
|
if (len == 0) {
|
|
return res;
|
|
}
|
|
jbyte *ptr = (*env)->GetByteArrayElements(env, arr, NULL);
|
|
if (ptr == NULL) {
|
|
LOG_FATAL("GetByteArrayElements failed");
|
|
return res;
|
|
}
|
|
if (copy) {
|
|
void *ptr_copy = (void *)malloc(len);
|
|
if (ptr_copy == NULL) {
|
|
LOG_FATAL("malloc failed");
|
|
return res;
|
|
}
|
|
memcpy(ptr_copy, ptr, len);
|
|
(*env)->ReleaseByteArrayElements(env, arr, ptr, JNI_ABORT);
|
|
ptr = (jbyte *)ptr_copy;
|
|
}
|
|
res.ptr = ptr;
|
|
res.len = len;
|
|
return res;
|
|
}
|
|
|
|
int32_t go_seq_to_refnum(JNIEnv *env, jobject o) {
|
|
if (o == NULL) {
|
|
return NULL_REFNUM;
|
|
}
|
|
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incRef, o);
|
|
}
|
|
|
|
jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons) {
|
|
if (refnum == NULL_REFNUM) {
|
|
return NULL;
|
|
}
|
|
// Seq.Ref ref = Seq.getRef(refnum)
|
|
jobject ref = (*env)->CallStaticObjectMethod(env, seq_class, seq_getRef, (jint)refnum);
|
|
if (ref == NULL) {
|
|
LOG_FATAL("Unknown reference: %d", refnum);
|
|
return NULL;
|
|
}
|
|
if (refnum > 0) { // Java object
|
|
// return ref.obj
|
|
return (*env)->GetObjectField(env, ref, ref_objField);
|
|
} else {
|
|
// return new <Proxy>(ref)
|
|
return (*env)->NewObject(env, proxy_class, proxy_cons, ref);
|
|
}
|
|
}
|
|
|
|
// go_seq_to_java_string converts a nstring to a jstring.
|
|
jstring go_seq_to_java_string(JNIEnv *env, nstring str) {
|
|
jstring s = (*env)->NewString(env, str.chars, str.len);
|
|
if (str.chars != NULL) {
|
|
free(str.chars);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread
|
|
// and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame.
|
|
JNIEnv *go_seq_push_local_frame(jint cap) {
|
|
JNIEnv *env = go_seq_get_thread_env();
|
|
if ((*env)->PushLocalFrame(env, cap) < 0) {
|
|
LOG_FATAL("PushLocalFrame failed");
|
|
}
|
|
return env;
|
|
}
|
|
|
|
// Pop the current local frame, freeing all JNI local references in it
|
|
void go_seq_pop_local_frame(JNIEnv *env) {
|
|
(*env)->PopLocalFrame(env, NULL);
|
|
}
|
|
|
|
void go_seq_dec_ref(int32_t ref) {
|
|
JNIEnv *env = go_seq_get_thread_env();
|
|
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_go_Seq_init(JNIEnv *env, jclass clazz) {
|
|
if ((*env)->GetJavaVM(env, &jvm) != 0) {
|
|
LOG_FATAL("failed to get JVM");
|
|
return;
|
|
}
|
|
if (pthread_key_create(&jnienvs, env_destructor) != 0) {
|
|
LOG_FATAL("failed to initialize jnienvs thread local storage");
|
|
return;
|
|
}
|
|
|
|
seq_class = (*env)->NewGlobalRef(env, clazz);
|
|
seq_throw_exc = (*env)->GetStaticMethodID(env, seq_class, "throwException", "(Ljava/lang/String;)V");
|
|
if (seq_throw_exc == NULL) {
|
|
LOG_FATAL("failed to find method Seq.throwException");
|
|
return;
|
|
}
|
|
|
|
seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
|
|
if (seq_getRef == NULL) {
|
|
LOG_FATAL("failed to find method Seq.getRef");
|
|
return;
|
|
}
|
|
seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V");
|
|
if (seq_decRef == NULL) {
|
|
LOG_FATAL("failed to find method Seq.decRef");
|
|
return;
|
|
}
|
|
seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Lgo/Seq$Object;)I");
|
|
if (seq_incRef == NULL) {
|
|
LOG_FATAL("failed to find method Seq.incRef");
|
|
return;
|
|
}
|
|
jclass throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
|
|
if (throwable_class == NULL) {
|
|
LOG_FATAL("failed to find Throwable class");
|
|
return;
|
|
}
|
|
throwable_getMessage = (*env)->GetMethodID(env, throwable_class, "getMessage", "()Ljava/lang/String;");
|
|
if (throwable_getMessage == NULL) {
|
|
LOG_FATAL("failed to find method Throwable.getMessage");
|
|
return;
|
|
}
|
|
jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
|
|
if (ref_class == NULL) {
|
|
LOG_FATAL("failed to find the Seq.Ref class");
|
|
return;
|
|
}
|
|
ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Lgo/Seq$Object;");
|
|
if (ref_objField == NULL) {
|
|
LOG_FATAL("failed to find the Seq.Ref.obj field");
|
|
return;
|
|
}
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) {
|
|
DestroyRef(refnum);
|
|
}
|