blob: d87f3bde90077c9b66672815361ba4fe13776fe8 [file] [log] [blame]
// +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)
}