github.com/cyrilou242/gomobile-java@v0.0.0-20220215185836-09daef25a210/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 <errno.h> 10 #include <jni.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <pthread.h> 16 #include <string.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, (void **)&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 printf("Unknown reference: %d\n", refnum); 261 LOG_FATAL("Unknown reference"); 262 } 263 // Go incremented the reference count just before passing the refnum. Decrement it here. 264 (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum); 265 // return ref.obj 266 return (*env)->GetObjectField(env, ref, ref_objField); 267 } 268 269 // go_seq_to_java_string converts a nstring to a jstring. 270 jstring go_seq_to_java_string(JNIEnv *env, nstring str) { 271 jstring s = (*env)->NewString(env, str.chars, str.len/2); 272 if (str.chars != NULL) { 273 free(str.chars); 274 } 275 return s; 276 } 277 278 // go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread 279 // and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame. 280 JNIEnv *go_seq_push_local_frame(jint nargs) { 281 JNIEnv *env = go_seq_get_thread_env(); 282 // Given the number of function arguments, compute a conservative bound for the minimal frame size. 283 // Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra 284 // extra space for the receiver, the return value, and exception (if any). 285 jint frameSize = 2*nargs + 10; 286 if ((*env)->PushLocalFrame(env, frameSize) < 0) { 287 LOG_FATAL("PushLocalFrame failed"); 288 } 289 return env; 290 } 291 292 // Pop the current local frame, freeing all JNI local references in it 293 void go_seq_pop_local_frame(JNIEnv *env) { 294 (*env)->PopLocalFrame(env, NULL); 295 } 296 297 void go_seq_inc_ref(int32_t ref) { 298 JNIEnv *env = go_seq_get_thread_env(); 299 (*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref); 300 } 301 302 void go_seq_dec_ref(int32_t ref) { 303 JNIEnv *env = go_seq_get_thread_env(); 304 (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref); 305 } 306 307 JNIEXPORT void JNICALL 308 Java_go_Seq_init(JNIEnv *env, jclass clazz) { 309 if ((*env)->GetJavaVM(env, &jvm) != 0) { 310 LOG_FATAL("failed to get JVM"); 311 } 312 if (pthread_key_create(&jnienvs, env_destructor) != 0) { 313 LOG_FATAL("failed to initialize jnienvs thread local storage"); 314 } 315 316 seq_class = (*env)->NewGlobalRef(env, clazz); 317 seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;"); 318 if (seq_getRef == NULL) { 319 LOG_FATAL("failed to find method Seq.getRef"); 320 } 321 seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V"); 322 if (seq_decRef == NULL) { 323 LOG_FATAL("failed to find method Seq.decRef"); 324 } 325 seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V"); 326 if (seq_incRefnum == NULL) { 327 LOG_FATAL("failed to find method Seq.incRefnum"); 328 } 329 seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I"); 330 if (seq_incRef == NULL) { 331 LOG_FATAL("failed to find method Seq.incRef"); 332 } 333 seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I"); 334 if (seq_incGoObjectRef == NULL) { 335 LOG_FATAL("failed to find method Seq.incGoObjectRef"); 336 } 337 jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref"); 338 if (ref_class == NULL) { 339 LOG_FATAL("failed to find the Seq.Ref class"); 340 } 341 ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;"); 342 if (ref_objField == NULL) { 343 LOG_FATAL("failed to find the Seq.Ref.obj field"); 344 } 345 initClasses(); 346 } 347 348 JNIEXPORT void JNICALL 349 Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) { 350 DestroyRef(refnum); 351 } 352 353 JNIEXPORT void JNICALL 354 Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) { 355 IncGoRef(refnum); 356 } 357 358 jclass go_seq_find_class(const char *name) { 359 JNIEnv *env = go_seq_push_local_frame(0); 360 jclass clazz = (*env)->FindClass(env, name); 361 if (clazz == NULL) { 362 (*env)->ExceptionClear(env); 363 } else { 364 clazz = (*env)->NewGlobalRef(env, clazz); 365 } 366 go_seq_pop_local_frame(env); 367 return clazz; 368 } 369 370 jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) { 371 JNIEnv *env = go_seq_push_local_frame(0); 372 jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig); 373 if (m == NULL) { 374 (*env)->ExceptionClear(env); 375 } 376 go_seq_pop_local_frame(env); 377 return m; 378 } 379 380 jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) { 381 JNIEnv *env = go_seq_push_local_frame(0); 382 jmethodID m = (*env)->GetMethodID(env, clazz, name, sig); 383 if (m == NULL) { 384 (*env)->ExceptionClear(env); 385 } 386 go_seq_pop_local_frame(env); 387 return m; 388 } 389 390 void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) { 391 if (ptr != NULL) { 392 (*env)->ReleaseByteArrayElements(env, arr, ptr, 0); 393 } 394 } 395 396 int go_seq_isinstanceof(jint refnum, jclass clazz) { 397 JNIEnv *env = go_seq_push_local_frame(0); 398 jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL); 399 jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz); 400 go_seq_pop_local_frame(env); 401 return isinst; 402 }