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