seq_android.c.support 11 KB

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