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