| // +build android |
| |
| package jni |
| |
| import ( |
| "errors" |
| "fmt" |
| "reflect" |
| "sync" |
| "unicode" |
| "unicode/utf8" |
| "unsafe" |
| |
| "veyron2/verror" |
| ) |
| |
| // #cgo LDFLAGS: -ljniwrapper |
| // #include <stdlib.h> |
| // #include "jni_wrapper.h" |
| // // CGO doesn't support variadic functions so we have to hard-code these |
| // // functions to match the invoking code. Ugh! |
| // static jobject CallNewVeyronExceptionObject(JNIEnv* env, jclass class, jmethodID mid, jstring msg, jstring id) { |
| // return (*env)->NewObject(env, class, mid, msg, id); |
| // } |
| // static jstring CallGetMessage(JNIEnv* env, jobject obj, jmethodID id) { |
| // return (jstring)(*env)->CallObjectMethod(env, obj, id); |
| // } |
| import "C" |
| |
| const ( |
| voidSign = "V" |
| boolSign = "Z" |
| longSign = "J" |
| stringSign = "Ljava/lang/String;" |
| objectSign = "Ljava/lang/Object;" |
| ) |
| |
| func arraySign(sign string) string { |
| return "[" + sign |
| } |
| |
| // refs stores references to instances of various Go types, namely instances |
| // that are referenced only by the Java code. The only purpose of this store |
| // is to prevent Go runtime from garbage collecting those instances. |
| var refs = newSafeSet() |
| |
| // goRef creates a new reference to the value addressed by the provided pointer. |
| // The value will remain referenced until it is explicitly unreferenced using |
| // goUnref(). |
| func goRef(valptr interface{}) { |
| if !isPointer(valptr) { |
| panic("must pass pointer value to goRef") |
| } |
| refs.insert(valptr) |
| } |
| |
| // goUnref removes a previously added reference to the value addressed by the |
| // provided pointer. If the value hasn't been ref-ed (a bug?), this unref will |
| // be a no-op. |
| func goUnref(valptr interface{}) { |
| if !isPointer(valptr) { |
| panic("must pass pointer value to goUnref") |
| } |
| refs.delete(valptr) |
| } |
| |
| // ptrValue returns the value of the pointer as a Java C.jlong type. |
| func ptrValue(ptr interface{}) C.jlong { |
| v := reflect.ValueOf(ptr) |
| if v.Kind() != reflect.Ptr { |
| panic("must pass pointer value to ptrValue") |
| } |
| return C.jlong(v.Pointer()) |
| } |
| |
| // ptr returns the pointer represented by the provided (Java C.jlong) value. |
| func ptr(ptrValue C.jlong) unsafe.Pointer { |
| return unsafe.Pointer(uintptr(ptrValue)) |
| } |
| |
| // isPointer returns true iff the provided value is a pointer. |
| func isPointer(val interface{}) bool { |
| return reflect.ValueOf(val).Kind() == reflect.Ptr |
| } |
| |
| // goString returns a Go string given the Java string. |
| func goString(env *C.JNIEnv, str C.jstring) string { |
| if str == nil { |
| return "" |
| } |
| cString := C.GetStringUTFChars(env, str, nil) |
| defer C.ReleaseStringUTFChars(env, str, cString) |
| return C.GoString(cString) |
| } |
| |
| // jString returns a Java string given the Go string. |
| func jString(env *C.JNIEnv, str string) C.jstring { |
| cString := C.CString(str) |
| defer C.free(unsafe.Pointer(cString)) |
| return C.NewStringUTF(env, cString) |
| } |
| |
| // goStringArray converts a Java string array to a go string array. |
| func goStringArray(env *C.JNIEnv, jStrArray C.jobjectArray) []string { |
| if jStrArray == nil { |
| return nil |
| } |
| length := C.GetArrayLength(env, C.jarray(jStrArray)) |
| ret := make([]string, int(length)) |
| for i := 0; i < int(length); i++ { |
| ret[i] = goString(env, C.jstring(C.GetObjectArrayElement(env, jStrArray, C.jsize(i)))) |
| } |
| return ret |
| } |
| |
| // jThrow throws a new Java exception of the provided type with the given message. |
| func jThrow(env *C.JNIEnv, class C.jclass, msg string) { |
| s := C.CString(msg) |
| defer C.free(unsafe.Pointer(s)) |
| C.ThrowNew(env, class, s) |
| } |
| |
| // jThrowV throws a new Java VeyronException corresponding to the given error. |
| func jThrowV(env *C.JNIEnv, err error) { |
| verr := verror.Convert(err) |
| id := jMethodID(env, jVeyronExceptionClass, "<init>", fmt.Sprintf("(%s%s)%s", stringSign, stringSign, voidSign)) |
| obj := C.jthrowable(C.CallNewVeyronExceptionObject(env, jVeyronExceptionClass, id, jString(env, verr.Error()), jString(env, string(verr.ErrorID())))) |
| C.Throw(env, obj) |
| } |
| |
| // jExceptionMsg returns the exception message if an exception occurred, or |
| // nil otherwise. |
| func jExceptionMsg(env *C.JNIEnv) error { |
| e := C.ExceptionOccurred(env) |
| if e == nil { // no exception |
| return nil |
| } |
| C.ExceptionClear(env) |
| id := jMethodID(env, jThrowableClass, "getMessage", fmt.Sprintf("()%s", stringSign)) |
| jMsg := C.CallGetMessage(env, C.jobject(e), id) |
| //return errors.New("blah") |
| return errors.New(goString(env, jMsg)) |
| } |
| |
| // camelCase converts ThisString to thisString. |
| func camelCase(s string) string { |
| if s == "" { |
| return "" |
| } |
| r, n := utf8.DecodeRuneInString(s) |
| return string(unicode.ToLower(r)) + s[n:] |
| } |
| |
| // jBoolField returns the value of the provided object's boolean field. |
| func jBoolField(env *C.JNIEnv, obj C.jobject, field string) bool { |
| cField := C.CString(field) |
| defer C.free(unsafe.Pointer(cField)) |
| cSig := C.CString(boolSign) |
| defer C.free(unsafe.Pointer(cSig)) |
| fid := C.GetFieldID(env, C.GetObjectClass(env, obj), cField, cSig) |
| return C.GetBooleanField(env, obj, fid) != C.JNI_FALSE |
| } |
| |
| // jStringField returns the value of the provided object's String field, |
| // as a Go string. |
| func jStringField(env *C.JNIEnv, obj C.jobject, field string) string { |
| cField := C.CString(field) |
| defer C.free(unsafe.Pointer(cField)) |
| cSig := C.CString(stringSign) |
| defer C.free(unsafe.Pointer(cSig)) |
| fid := C.GetFieldID(env, C.GetObjectClass(env, obj), cField, cSig) |
| jStr := C.jstring(C.GetObjectField(env, obj, fid)) |
| return goString(env, jStr) |
| } |
| |
| // jStringArrayField returns the value of the provided object's String[] field, |
| // as a slice of Go strings. |
| func jStringArrayField(env *C.JNIEnv, obj C.jobject, field string) []string { |
| cField := C.CString(field) |
| defer C.free(unsafe.Pointer(cField)) |
| cSig := C.CString("[" + stringSign) |
| defer C.free(unsafe.Pointer(cSig)) |
| fid := C.GetFieldID(env, C.GetObjectClass(env, obj), cField, cSig) |
| jStrArray := C.jobjectArray(C.GetObjectField(env, obj, fid)) |
| return goStringArray(env, jStrArray) |
| } |
| |
| // jMethodID returns the Java method ID for the given method. |
| func jMethodID(env *C.JNIEnv, class C.jclass, name, signature string) C.jmethodID { |
| cName := C.CString(name) |
| defer C.free(unsafe.Pointer(cName)) |
| cSignature := C.CString(signature) |
| defer C.free(unsafe.Pointer(cSignature)) |
| return C.GetMethodID(env, class, cName, cSignature) |
| } |
| |
| // jFindClassOrDie returns the global references to the Java class with the |
| // given pathname, or panic-s if the class cannot be found. |
| func jFindClassOrDie(env *C.JNIEnv, name string) C.jclass { |
| cName := C.CString(name) |
| defer C.free(unsafe.Pointer(cName)) |
| class := C.FindClass(env, cName) |
| if err := jExceptionMsg(env); err != nil || class == nil { |
| panic(fmt.Sprintf("couldn't find class %s: %v", name, err)) |
| } |
| return C.jclass(C.NewGlobalRef(env, C.jobject(class))) |
| } |
| |
| // derefOrDie dereferences the provided (pointer) value, or panic-s if the value |
| // isn't of pointer type. |
| func derefOrDie(i interface{}) interface{} { |
| v := reflect.ValueOf(i) |
| if v.Kind() != reflect.Ptr { |
| panic(fmt.Sprintf("want reflect.Ptr value for %v, have %v", i, v.Kind())) |
| } |
| return v.Elem().Interface() |
| } |
| |
| func newSafeSet() *safeSet { |
| return &safeSet{ |
| items: make(map[interface{}]bool), |
| } |
| } |
| |
| // safeSet is a thread-safe set. |
| type safeSet struct { |
| lock sync.Mutex |
| items map[interface{}]bool |
| } |
| |
| func (s *safeSet) insert(item interface{}) { |
| s.lock.Lock() |
| defer s.lock.Unlock() |
| s.items[item] = true |
| } |
| |
| func (s *safeSet) delete(item interface{}) { |
| s.lock.Lock() |
| defer s.lock.Unlock() |
| delete(s.items, item) |
| } |