blob: e1f5dfaaeb942f47872e74dd084d7dfcf91de57b [file] [log] [blame]
// +build android
package main
import (
"errors"
"fmt"
"sync"
"unicode"
"unicode/utf8"
"unsafe"
"veyron2/verror"
)
// #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"
stringSign = "Ljava/lang/String;"
objectSign = "Ljava/lang/Object;"
)
// safeSet is a thread-safe set.
type safeSet struct {
lock sync.Mutex
items map[interface{}]bool
}
func newSafeSet() *safeSet {
return &safeSet{
items: make(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)
}
// 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)
}
// jThrow throws a new Java VeyronException with the given message.
func jThrow(env *C.JNIEnv, msg string) {
s := C.CString(msg)
defer C.free(unsafe.Pointer(s))
C.ThrowNew(env, jVeyronExceptionClass, s)
}
// jThrowV throws a new Java VeyronException corresponding to the given verror.
func jThrowV(env *C.JNIEnv, err verror.E) {
id := jMethodID(env, jVeyronExceptionClass, "<init>", fmt.Sprintf("(%s%s)%s", stringSign, stringSign, voidSign))
obj := C.jthrowable(C.CallNewVeyronExceptionObject(env, jVeyronExceptionClass, id, jString(env, err.Error()), jString(env, string(err.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))
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
}
// 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)))
}