github.com/danbrough/mobile@v0.0.3-beta03/bind/java/seq_support.c.support (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // C support functions for bindings. This file is copied into the 6 // generated gomobile_bind package and compiled along with the 7 // generated binding files. 8 9 #ifdef __GOBIND_ANDROID__ 10 #include <android/log.h> 11 #endif 12 #include <errno.h> 13 #include <jni.h> 14 #include <stdint.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <unistd.h> 18 #include <pthread.h> 19 #include "seq_support.h" 20 #include "_cgo_export.h" 21 22 #define NULL_REFNUM 41 23 24 // initClasses are only exported from Go if reverse bindings are used. 25 // If they're not, weakly define a no-op function. 26 __attribute__((weak)) void initClasses(void) { } 27 28 static JavaVM *jvm; 29 // jnienvs holds the per-thread JNIEnv* for Go threads where we called AttachCurrentThread. 30 // A pthread key destructor is supplied to call DetachCurrentThread on exit. This trick is 31 // documented in http://developer.android.com/training/articles/perf-jni.html under "Threads". 32 static pthread_key_t jnienvs; 33 34 static jclass seq_class; 35 static jmethodID seq_getRef; 36 static jmethodID seq_decRef; 37 static jmethodID seq_incRef; 38 static jmethodID seq_incGoObjectRef; 39 static jmethodID seq_incRefnum; 40 41 static jfieldID ref_objField; 42 43 static jclass throwable_class; 44 45 // env_destructor is registered as a thread data key destructor to 46 // clean up a Go thread that is attached to the JVM. 47 static void env_destructor(void *env) { 48 if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) { 49 LOG_INFO("failed to detach current thread"); 50 } 51 } 52 53 static JNIEnv *go_seq_get_thread_env(void) { 54 JNIEnv *env; 55 jint ret = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6); 56 if (ret != JNI_OK) { 57 if (ret != JNI_EDETACHED) { 58 LOG_FATAL("failed to get thread env"); 59 } 60 #ifdef __GOBIND_ANDROID__ 61 if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) { 62 #else 63 if ((*jvm)->AttachCurrentThread(jvm, (void**) &env, NULL) != JNI_OK) { 64 #endif 65 LOG_FATAL("failed to attach current thread"); 66 } 67 pthread_setspecific(jnienvs, env); 68 } 69 return env; 70 } 71 72 void go_seq_maybe_throw_exception(JNIEnv *env, jobject exc) { 73 if (exc != NULL) { 74 (*env)->Throw(env, exc); 75 } 76 } 77 78 jobject go_seq_get_exception(JNIEnv *env) { 79 jthrowable exc = (*env)->ExceptionOccurred(env); 80 if (!exc) { 81 return NULL; 82 } 83 (*env)->ExceptionClear(env); 84 return exc; 85 } 86 87 jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) { 88 if (s.ptr == NULL) { 89 return NULL; 90 } 91 jbyteArray res = (*env)->NewByteArray(env, s.len); 92 if (res == NULL) { 93 LOG_FATAL("NewByteArray failed"); 94 } 95 (*env)->SetByteArrayRegion(env, res, 0, s.len, s.ptr); 96 if (copy) { 97 free(s.ptr); 98 } 99 return res; 100 } 101 102 #define surr1 0xd800 103 #define surr2 0xdc00 104 #define surr3 0xe000 105 106 // Unicode replacement character 107 #define replacementChar 0xFFFD 108 109 #define rune1Max ((1<<7) - 1) 110 #define rune2Max ((1<<11) - 1) 111 #define rune3Max ((1<<16) - 1) 112 // Maximum valid Unicode code point. 113 #define MaxRune 0x0010FFFF 114 115 #define surrogateMin 0xD800 116 #define surrogateMax 0xDFFF 117 // 0011 1111 118 #define maskx 0x3F 119 // 1000 0000 120 #define tx 0x80 121 // 1100 0000 122 #define t2 0xC0 123 // 1110 0000 124 #define t3 0xE0 125 // 1111 0000 126 #define t4 0xF0 127 128 // encode_rune writes into p (which must be large enough) the UTF-8 encoding 129 // of the rune. It returns the number of bytes written. 130 static int encode_rune(uint8_t *p, uint32_t r) { 131 if (r <= rune1Max) { 132 p[0] = (uint8_t)r; 133 return 1; 134 } else if (r <= rune2Max) { 135 p[0] = t2 | (uint8_t)(r>>6); 136 p[1] = tx | (((uint8_t)(r))&maskx); 137 return 2; 138 } else { 139 if (r > MaxRune || (surrogateMin <= r && r <= surrogateMax)) { 140 r = replacementChar; 141 } 142 if (r <= rune3Max) { 143 p[0] = t3 | (uint8_t)(r>>12); 144 p[1] = tx | (((uint8_t)(r>>6))&maskx); 145 p[2] = tx | (((uint8_t)(r))&maskx); 146 return 3; 147 } else { 148 p[0] = t4 | (uint8_t)(r>>18); 149 p[1] = tx | (((uint8_t)(r>>12))&maskx); 150 p[2] = tx | (((uint8_t)(r>>6))&maskx); 151 p[3] = tx | (((uint8_t)(r))&maskx); 152 return 4; 153 } 154 } 155 } 156 157 // utf16_decode decodes an array of UTF16 characters to a UTF-8 encoded 158 // nstring copy. The support functions and utf16_decode itself are heavily 159 // based on the unicode/utf8 and unicode/utf16 Go packages. 160 static nstring utf16_decode(jchar *chars, jsize len) { 161 jsize worstCaseLen = 4*len; 162 uint8_t *buf = malloc(worstCaseLen); 163 if (buf == NULL) { 164 LOG_FATAL("utf16Decode: malloc failed"); 165 } 166 jsize nsrc = 0; 167 jsize ndst = 0; 168 while (nsrc < len) { 169 uint32_t r = chars[nsrc]; 170 nsrc++; 171 if (surr1 <= r && r < surr2 && nsrc < len) { 172 uint32_t r2 = chars[nsrc]; 173 if (surr2 <= r2 && r2 < surr3) { 174 nsrc++; 175 r = (((r-surr1)<<10) | (r2 - surr2)) + 0x10000; 176 } 177 } 178 if (ndst + 4 > worstCaseLen) { 179 LOG_FATAL("utf16Decode: buffer overflow"); 180 } 181 ndst += encode_rune(buf + ndst, r); 182 } 183 struct nstring res = {.chars = buf, .len = ndst}; 184 return res; 185 } 186 187 nstring go_seq_from_java_string(JNIEnv *env, jstring str) { 188 struct nstring res = {NULL, 0}; 189 if (str == NULL) { 190 return res; 191 } 192 jsize nchars = (*env)->GetStringLength(env, str); 193 if (nchars == 0) { 194 return res; 195 } 196 jchar *chars = (jchar *)(*env)->GetStringChars(env, str, NULL); 197 if (chars == NULL) { 198 LOG_FATAL("GetStringChars failed"); 199 } 200 nstring nstr = utf16_decode(chars, nchars); 201 (*env)->ReleaseStringChars(env, str, chars); 202 return nstr; 203 } 204 205 nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) { 206 struct nbyteslice res = {NULL, 0}; 207 if (arr == NULL) { 208 return res; 209 } 210 211 jsize len = (*env)->GetArrayLength(env, arr); 212 if (len == 0) { 213 return res; 214 } 215 jbyte *ptr = (*env)->GetByteArrayElements(env, arr, NULL); 216 if (ptr == NULL) { 217 LOG_FATAL("GetByteArrayElements failed"); 218 } 219 if (copy) { 220 void *ptr_copy = (void *)malloc(len); 221 if (ptr_copy == NULL) { 222 LOG_FATAL("malloc failed"); 223 } 224 memcpy(ptr_copy, ptr, len); 225 (*env)->ReleaseByteArrayElements(env, arr, ptr, JNI_ABORT); 226 ptr = (jbyte *)ptr_copy; 227 } 228 res.ptr = ptr; 229 res.len = len; 230 return res; 231 } 232 233 int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) { 234 if (o == NULL) { 235 return NULL_REFNUM; 236 } 237 return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incGoObjectRef, o); 238 } 239 240 int32_t go_seq_to_refnum(JNIEnv *env, jobject o) { 241 if (o == NULL) { 242 return NULL_REFNUM; 243 } 244 return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incRef, o); 245 } 246 247 int32_t go_seq_unwrap(jint refnum) { 248 JNIEnv *env = go_seq_push_local_frame(0); 249 jobject jobj = go_seq_from_refnum(env, refnum, NULL, NULL); 250 int32_t goref = go_seq_to_refnum_go(env, jobj); 251 go_seq_pop_local_frame(env); 252 return goref; 253 } 254 255 jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons) { 256 if (refnum == NULL_REFNUM) { 257 return NULL; 258 } 259 if (refnum < 0) { // Go object 260 // return new <Proxy>(refnum) 261 return (*env)->NewObject(env, proxy_class, proxy_cons, refnum); 262 } 263 // Seq.Ref ref = Seq.getRef(refnum) 264 jobject ref = (*env)->CallStaticObjectMethod(env, seq_class, seq_getRef, (jint)refnum); 265 if (ref == NULL) { 266 LOG_FATAL("Unknown reference: %d", refnum); 267 } 268 // Go incremented the reference count just before passing the refnum. Decrement it here. 269 (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum); 270 // return ref.obj 271 return (*env)->GetObjectField(env, ref, ref_objField); 272 } 273 274 // go_seq_to_java_string converts a nstring to a jstring. 275 jstring go_seq_to_java_string(JNIEnv *env, nstring str) { 276 jstring s = (*env)->NewString(env, str.chars, str.len/2); 277 if (str.chars != NULL) { 278 free(str.chars); 279 } 280 return s; 281 } 282 283 // go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread 284 // and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame. 285 JNIEnv *go_seq_push_local_frame(jint nargs) { 286 JNIEnv *env = go_seq_get_thread_env(); 287 // Given the number of function arguments, compute a conservative bound for the minimal frame size. 288 // Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra 289 // extra space for the receiver, the return value, and exception (if any). 290 jint frameSize = 2*nargs + 10; 291 if ((*env)->PushLocalFrame(env, frameSize) < 0) { 292 LOG_FATAL("PushLocalFrame failed"); 293 } 294 return env; 295 } 296 297 // Pop the current local frame, freeing all JNI local references in it 298 void go_seq_pop_local_frame(JNIEnv *env) { 299 (*env)->PopLocalFrame(env, NULL); 300 } 301 302 void go_seq_inc_ref(int32_t ref) { 303 JNIEnv *env = go_seq_get_thread_env(); 304 (*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref); 305 } 306 307 void go_seq_dec_ref(int32_t ref) { 308 JNIEnv *env = go_seq_get_thread_env(); 309 (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref); 310 } 311 312 JNIEXPORT void JNICALL 313 Java_go_Seq_init(JNIEnv *env, jclass clazz) { 314 if ((*env)->GetJavaVM(env, &jvm) != 0) { 315 LOG_FATAL("failed to get JVM"); 316 } 317 if (pthread_key_create(&jnienvs, env_destructor) != 0) { 318 LOG_FATAL("failed to initialize jnienvs thread local storage"); 319 } 320 321 seq_class = (*env)->NewGlobalRef(env, clazz); 322 seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;"); 323 if (seq_getRef == NULL) { 324 LOG_FATAL("failed to find method Seq.getRef"); 325 } 326 seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V"); 327 if (seq_decRef == NULL) { 328 LOG_FATAL("failed to find method Seq.decRef"); 329 } 330 seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V"); 331 if (seq_incRefnum == NULL) { 332 LOG_FATAL("failed to find method Seq.incRefnum"); 333 } 334 seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I"); 335 if (seq_incRef == NULL) { 336 LOG_FATAL("failed to find method Seq.incRef"); 337 } 338 seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I"); 339 if (seq_incGoObjectRef == NULL) { 340 LOG_FATAL("failed to find method Seq.incGoObjectRef"); 341 } 342 jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref"); 343 if (ref_class == NULL) { 344 LOG_FATAL("failed to find the Seq.Ref class"); 345 } 346 ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;"); 347 if (ref_objField == NULL) { 348 LOG_FATAL("failed to find the Seq.Ref.obj field"); 349 } 350 initClasses(); 351 } 352 353 JNIEXPORT void JNICALL 354 Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) { 355 DestroyRef(refnum); 356 } 357 358 JNIEXPORT void JNICALL 359 Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) { 360 IncGoRef(refnum); 361 } 362 363 jclass go_seq_find_class(const char *name) { 364 JNIEnv *env = go_seq_push_local_frame(0); 365 jclass clazz = (*env)->FindClass(env, name); 366 if (clazz == NULL) { 367 (*env)->ExceptionClear(env); 368 } else { 369 clazz = (*env)->NewGlobalRef(env, clazz); 370 } 371 go_seq_pop_local_frame(env); 372 return clazz; 373 } 374 375 jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) { 376 JNIEnv *env = go_seq_push_local_frame(0); 377 jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig); 378 if (m == NULL) { 379 (*env)->ExceptionClear(env); 380 } 381 go_seq_pop_local_frame(env); 382 return m; 383 } 384 385 jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) { 386 JNIEnv *env = go_seq_push_local_frame(0); 387 jmethodID m = (*env)->GetMethodID(env, clazz, name, sig); 388 if (m == NULL) { 389 (*env)->ExceptionClear(env); 390 } 391 go_seq_pop_local_frame(env); 392 return m; 393 } 394 395 void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) { 396 if (ptr != NULL) { 397 (*env)->ReleaseByteArrayElements(env, arr, ptr, 0); 398 } 399 } 400 401 int go_seq_isinstanceof(jint refnum, jclass clazz) { 402 JNIEnv *env = go_seq_push_local_frame(0); 403 jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL); 404 jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz); 405 go_seq_pop_local_frame(env); 406 return isinst; 407 }