github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/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  }