seq_android.c.support 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. // [Psiphon]
  234. // - Check for thrown exception, and abort log its name and message instead of "Unknown reference"
  235. jthrowable exception = (*env)->ExceptionOccurred(env);
  236. if (exception != NULL) {
  237. (*env)->ExceptionDescribe(env);
  238. (*env)->ExceptionClear(env);
  239. jclass throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
  240. jmethodID to_string_method = (*env)->GetMethodID(env, throwable_class, "toString", "()Ljava/lang/String;");
  241. jstring to_string = (jstring)((*env)->CallObjectMethod(env, exception, to_string_method));
  242. LOG_FATAL("Seq.getRef exception: %s", (*env)->GetStringUTFChars(env, to_string, JNI_FALSE));
  243. }
  244. // [Psiphon]
  245. if (ref == NULL) {
  246. LOG_FATAL("Unknown reference: %d", refnum);
  247. }
  248. // Go incremented the reference count just before passing the refnum. Decrement it here.
  249. (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum);
  250. // return ref.obj
  251. return (*env)->GetObjectField(env, ref, ref_objField);
  252. }
  253. // go_seq_to_java_string converts a nstring to a jstring.
  254. jstring go_seq_to_java_string(JNIEnv *env, nstring str) {
  255. jstring s = (*env)->NewString(env, str.chars, str.len/2);
  256. if (str.chars != NULL) {
  257. free(str.chars);
  258. }
  259. return s;
  260. }
  261. // go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread
  262. // and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame.
  263. JNIEnv *go_seq_push_local_frame(jint nargs) {
  264. JNIEnv *env = go_seq_get_thread_env();
  265. // Given the number of function arguments, compute a conservative bound for the minimal frame size.
  266. // Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra
  267. // extra space for the receiver, the return value, and exception (if any).
  268. jint frameSize = 2*nargs + 10;
  269. if ((*env)->PushLocalFrame(env, frameSize) < 0) {
  270. LOG_FATAL("PushLocalFrame failed");
  271. }
  272. return env;
  273. }
  274. // Pop the current local frame, freeing all JNI local references in it
  275. void go_seq_pop_local_frame(JNIEnv *env) {
  276. (*env)->PopLocalFrame(env, NULL);
  277. }
  278. void go_seq_inc_ref(int32_t ref) {
  279. JNIEnv *env = go_seq_get_thread_env();
  280. (*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref);
  281. }
  282. void go_seq_dec_ref(int32_t ref) {
  283. JNIEnv *env = go_seq_get_thread_env();
  284. (*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref);
  285. }
  286. JNIEXPORT void JNICALL
  287. Java_go_Seq_init(JNIEnv *env, jclass clazz) {
  288. if ((*env)->GetJavaVM(env, &jvm) != 0) {
  289. LOG_FATAL("failed to get JVM");
  290. }
  291. if (pthread_key_create(&jnienvs, env_destructor) != 0) {
  292. LOG_FATAL("failed to initialize jnienvs thread local storage");
  293. }
  294. seq_class = (*env)->NewGlobalRef(env, clazz);
  295. seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
  296. if (seq_getRef == NULL) {
  297. LOG_FATAL("failed to find method Seq.getRef");
  298. }
  299. seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V");
  300. if (seq_decRef == NULL) {
  301. LOG_FATAL("failed to find method Seq.decRef");
  302. }
  303. seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V");
  304. if (seq_incRefnum == NULL) {
  305. LOG_FATAL("failed to find method Seq.incRefnum");
  306. }
  307. seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I");
  308. if (seq_incRef == NULL) {
  309. LOG_FATAL("failed to find method Seq.incRef");
  310. }
  311. seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I");
  312. if (seq_incGoObjectRef == NULL) {
  313. LOG_FATAL("failed to find method Seq.incGoObjectRef");
  314. }
  315. jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
  316. if (ref_class == NULL) {
  317. LOG_FATAL("failed to find the Seq.Ref class");
  318. }
  319. ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;");
  320. if (ref_objField == NULL) {
  321. LOG_FATAL("failed to find the Seq.Ref.obj field");
  322. }
  323. initClasses();
  324. }
  325. JNIEXPORT void JNICALL
  326. Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) {
  327. DestroyRef(refnum);
  328. }
  329. JNIEXPORT void JNICALL
  330. Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) {
  331. IncGoRef(refnum);
  332. }
  333. jclass go_seq_find_class(const char *name) {
  334. JNIEnv *env = go_seq_push_local_frame(0);
  335. jclass clazz = (*env)->FindClass(env, name);
  336. if (clazz == NULL) {
  337. (*env)->ExceptionClear(env);
  338. } else {
  339. clazz = (*env)->NewGlobalRef(env, clazz);
  340. }
  341. go_seq_pop_local_frame(env);
  342. return clazz;
  343. }
  344. jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) {
  345. JNIEnv *env = go_seq_push_local_frame(0);
  346. jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
  347. if (m == NULL) {
  348. (*env)->ExceptionClear(env);
  349. }
  350. go_seq_pop_local_frame(env);
  351. return m;
  352. }
  353. jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) {
  354. JNIEnv *env = go_seq_push_local_frame(0);
  355. jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
  356. if (m == NULL) {
  357. (*env)->ExceptionClear(env);
  358. }
  359. go_seq_pop_local_frame(env);
  360. return m;
  361. }
  362. void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) {
  363. if (ptr != NULL) {
  364. (*env)->ReleaseByteArrayElements(env, arr, ptr, 0);
  365. }
  366. }
  367. int go_seq_isinstanceof(jint refnum, jclass clazz) {
  368. JNIEnv *env = go_seq_push_local_frame(0);
  369. jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL);
  370. jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz);
  371. go_seq_pop_local_frame(env);
  372. return isinst;
  373. }