| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- // 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 <string.h>
- #include <unistd.h>
- #include <pthread.h>
- #include "seq.h"
- #include "_cgo_export.h"
- #define NULL_REFNUM 41
- // initClasses are only exported from Go if reverse bindings are used.
- // If they're not, weakly define a no-op function.
- __attribute__((weak)) void initClasses(void) { }
- 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_getRef;
- static jmethodID seq_decRef;
- static jmethodID seq_incRef;
- static jmethodID seq_incGoObjectRef;
- static jmethodID seq_incRefnum;
- static jfieldID ref_objField;
- static jclass throwable_class;
- // 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");
- }
- if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) {
- LOG_FATAL("failed to attach current thread");
- }
- pthread_setspecific(jnienvs, env);
- }
- return env;
- }
- void go_seq_maybe_throw_exception(JNIEnv *env, jobject exc) {
- if (exc != NULL) {
- (*env)->Throw(env, exc);
- }
- }
- jobject go_seq_get_exception(JNIEnv *env) {
- jthrowable exc = (*env)->ExceptionOccurred(env);
- if (!exc) {
- return NULL;
- }
- (*env)->ExceptionClear(env);
- return exc;
- }
- 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");
- }
- (*env)->SetByteArrayRegion(env, res, 0, s.len, s.ptr);
- if (copy) {
- free(s.ptr);
- }
- return res;
- }
- #define surr1 0xd800
- #define surr2 0xdc00
- #define surr3 0xe000
- // Unicode replacement character
- #define replacementChar 0xFFFD
- #define rune1Max ((1<<7) - 1)
- #define rune2Max ((1<<11) - 1)
- #define rune3Max ((1<<16) - 1)
- // Maximum valid Unicode code point.
- #define MaxRune 0x0010FFFF
- #define surrogateMin 0xD800
- #define surrogateMax 0xDFFF
- // 0011 1111
- #define maskx 0x3F
- // 1000 0000
- #define tx 0x80
- // 1100 0000
- #define t2 0xC0
- // 1110 0000
- #define t3 0xE0
- // 1111 0000
- #define t4 0xF0
- // encode_rune writes into p (which must be large enough) the UTF-8 encoding
- // of the rune. It returns the number of bytes written.
- static int encode_rune(uint8_t *p, uint32_t r) {
- if (r <= rune1Max) {
- p[0] = (uint8_t)r;
- return 1;
- } else if (r <= rune2Max) {
- p[0] = t2 | (uint8_t)(r>>6);
- p[1] = tx | (((uint8_t)(r))&maskx);
- return 2;
- } else {
- if (r > MaxRune || (surrogateMin <= r && r <= surrogateMax)) {
- r = replacementChar;
- }
- if (r <= rune3Max) {
- p[0] = t3 | (uint8_t)(r>>12);
- p[1] = tx | (((uint8_t)(r>>6))&maskx);
- p[2] = tx | (((uint8_t)(r))&maskx);
- return 3;
- } else {
- p[0] = t4 | (uint8_t)(r>>18);
- p[1] = tx | (((uint8_t)(r>>12))&maskx);
- p[2] = tx | (((uint8_t)(r>>6))&maskx);
- p[3] = tx | (((uint8_t)(r))&maskx);
- return 4;
- }
- }
- }
- // utf16_decode decodes an array of UTF16 characters to a UTF-8 encoded
- // nstring copy. The support functions and utf16_decode itself are heavily
- // based on the unicode/utf8 and unicode/utf16 Go packages.
- static nstring utf16_decode(jchar *chars, jsize len) {
- jsize worstCaseLen = 4*len;
- uint8_t *buf = malloc(worstCaseLen);
- if (buf == NULL) {
- LOG_FATAL("utf16Decode: malloc failed");
- }
- jsize nsrc = 0;
- jsize ndst = 0;
- while (nsrc < len) {
- uint32_t r = chars[nsrc];
- nsrc++;
- if (surr1 <= r && r < surr2 && nsrc < len) {
- uint32_t r2 = chars[nsrc];
- if (surr2 <= r2 && r2 < surr3) {
- nsrc++;
- r = (((r-surr1)<<10) | (r2 - surr2)) + 0x10000;
- }
- }
- if (ndst + 4 > worstCaseLen) {
- LOG_FATAL("utf16Decode: buffer overflow");
- }
- ndst += encode_rune(buf + ndst, r);
- }
- struct nstring res = {.chars = buf, .len = ndst};
- return res;
- }
- nstring go_seq_from_java_string(JNIEnv *env, jstring str) {
- struct nstring res = {NULL, 0};
- if (str == NULL) {
- return res;
- }
- jsize nchars = (*env)->GetStringLength(env, str);
- if (nchars == 0) {
- return res;
- }
- jchar *chars = (jchar *)(*env)->GetStringChars(env, str, NULL);
- if (chars == NULL) {
- LOG_FATAL("GetStringChars failed");
- }
- nstring nstr = utf16_decode(chars, nchars);
- (*env)->ReleaseStringChars(env, str, chars);
- return nstr;
- }
- 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");
- }
- if (copy) {
- void *ptr_copy = (void *)malloc(len);
- if (ptr_copy == NULL) {
- LOG_FATAL("malloc failed");
- }
- 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_go(JNIEnv *env, jobject o) {
- if (o == NULL) {
- return NULL_REFNUM;
- }
- return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incGoObjectRef, o);
- }
- 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);
- }
- int32_t go_seq_unwrap(jint refnum) {
- JNIEnv *env = go_seq_push_local_frame(0);
- jobject jobj = go_seq_from_refnum(env, refnum, NULL, NULL);
- int32_t goref = go_seq_to_refnum_go(env, jobj);
- go_seq_pop_local_frame(env);
- return goref;
- }
- jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons) {
- if (refnum == NULL_REFNUM) {
- return NULL;
- }
- if (refnum < 0) { // Go object
- // return new <Proxy>(refnum)
- return (*env)->NewObject(env, proxy_class, proxy_cons, refnum);
- }
- // Seq.Ref ref = Seq.getRef(refnum)
- jobject ref = (*env)->CallStaticObjectMethod(env, seq_class, seq_getRef, (jint)refnum);
- // [Psiphon]
- // - Check for thrown exception, and abort log its name and message instead of "Unknown reference"
- jthrowable exception = (*env)->ExceptionOccurred(env);
- if (exception != NULL) {
- (*env)->ExceptionDescribe(env);
- (*env)->ExceptionClear(env);
- jclass throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
- jmethodID to_string_method = (*env)->GetMethodID(env, throwable_class, "toString", "()Ljava/lang/String;");
- jstring to_string = (jstring)((*env)->CallObjectMethod(env, exception, to_string_method));
- LOG_FATAL("Seq.getRef exception: %s", (*env)->GetStringUTFChars(env, to_string, JNI_FALSE));
- }
- // [Psiphon]
- if (ref == NULL) {
- LOG_FATAL("Unknown reference: %d", refnum);
- }
- // Go incremented the reference count just before passing the refnum. Decrement it here.
- (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum);
- // return ref.obj
- return (*env)->GetObjectField(env, ref, ref_objField);
- }
- // 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/2);
- 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 nargs) {
- JNIEnv *env = go_seq_get_thread_env();
- // Given the number of function arguments, compute a conservative bound for the minimal frame size.
- // Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra
- // extra space for the receiver, the return value, and exception (if any).
- jint frameSize = 2*nargs + 10;
- if ((*env)->PushLocalFrame(env, frameSize) < 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_inc_ref(int32_t ref) {
- JNIEnv *env = go_seq_get_thread_env();
- (*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref);
- }
- 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");
- }
- if (pthread_key_create(&jnienvs, env_destructor) != 0) {
- LOG_FATAL("failed to initialize jnienvs thread local storage");
- }
- seq_class = (*env)->NewGlobalRef(env, clazz);
- seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
- if (seq_getRef == NULL) {
- LOG_FATAL("failed to find method Seq.getRef");
- }
- seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V");
- if (seq_decRef == NULL) {
- LOG_FATAL("failed to find method Seq.decRef");
- }
- seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V");
- if (seq_incRefnum == NULL) {
- LOG_FATAL("failed to find method Seq.incRefnum");
- }
- seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I");
- if (seq_incRef == NULL) {
- LOG_FATAL("failed to find method Seq.incRef");
- }
- seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I");
- if (seq_incGoObjectRef == NULL) {
- LOG_FATAL("failed to find method Seq.incGoObjectRef");
- }
- jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
- if (ref_class == NULL) {
- LOG_FATAL("failed to find the Seq.Ref class");
- }
- ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;");
- if (ref_objField == NULL) {
- LOG_FATAL("failed to find the Seq.Ref.obj field");
- }
- initClasses();
- }
- JNIEXPORT void JNICALL
- Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) {
- DestroyRef(refnum);
- }
- JNIEXPORT void JNICALL
- Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) {
- IncGoRef(refnum);
- }
- jclass go_seq_find_class(const char *name) {
- JNIEnv *env = go_seq_push_local_frame(0);
- jclass clazz = (*env)->FindClass(env, name);
- if (clazz == NULL) {
- (*env)->ExceptionClear(env);
- } else {
- clazz = (*env)->NewGlobalRef(env, clazz);
- }
- go_seq_pop_local_frame(env);
- return clazz;
- }
- jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) {
- JNIEnv *env = go_seq_push_local_frame(0);
- jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
- if (m == NULL) {
- (*env)->ExceptionClear(env);
- }
- go_seq_pop_local_frame(env);
- return m;
- }
- jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) {
- JNIEnv *env = go_seq_push_local_frame(0);
- jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
- if (m == NULL) {
- (*env)->ExceptionClear(env);
- }
- go_seq_pop_local_frame(env);
- return m;
- }
- void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) {
- if (ptr != NULL) {
- (*env)->ReleaseByteArrayElements(env, arr, ptr, 0);
- }
- }
- int go_seq_isinstanceof(jint refnum, jclass clazz) {
- JNIEnv *env = go_seq_push_local_frame(0);
- jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL);
- jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz);
- go_seq_pop_local_frame(env);
- return isinst;
- }
|