blob: 735fb283ccbcc3b8da686d6a715a4c87919afebd [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build java android
package util
import (
"fmt"
"unsafe"
)
// #include <stdlib.h>
// #include "jni_wrapper.h"
//
// static jvalue* allocJValueArray(int elements) {
// return malloc(sizeof(jvalue) * elements);
// }
//
// static void setJValueArrayElement(jvalue* arr, int index, jvalue val) {
// arr[index] = val;
// }
//
import "C"
// jArgArray converts a slice of Go args to an array of Java args. It uses the provided slice of
// Signs to validate that the arguments are of compatible types.
func jArgArray(env *C.JNIEnv, args []interface{}, argSigns []Sign) (jArr *C.jvalue, free func(), err error) {
if len(argSigns) != len(args) {
return (*C.jvalue)(nil), nil, fmt.Errorf("mismatch in number of arguments, want %d, got %d", len(argSigns), len(args))
}
jvalueArr := C.allocJValueArray(C.int(len(args)))
for i, arg := range args {
sign := argSigns[i]
jVal, ok := jValue(env, arg, sign)
if !ok {
return (*C.jvalue)(nil), nil, fmt.Errorf("couldn't get Java value for argument #%d [%v] of expected type %v", i, arg, sign)
}
C.setJValueArrayElement(jvalueArr, C.int(i), jVal)
}
freeFunc := func() {
C.free(unsafe.Pointer(jvalueArr))
}
return jvalueArr, freeFunc, nil
}
// NewObject calls a java constructor through JNI, passing the specified args.
func NewObject(env interface{}, class interface{}, argSigns []Sign, args ...interface{}) (unsafe.Pointer, error) {
if class == nil {
panic("cannot call constructor of nil class")
}
jenv := getEnv(env)
jclass := getClass(class)
jcid, err := JMethodID(jenv, jclass, "<init>", FuncSign(argSigns, VoidSign))
if err != nil {
return nil, err
}
valArray, freeFunc, err := jArgArray(jenv, args, argSigns)
if err != nil {
return nil, err
}
defer freeFunc()
ret := C.NewObjectA(jenv, jclass, C.jmethodID(jcid), valArray)
err = JExceptionMsg(env)
return unsafe.Pointer(ret), err
}
// setupMethodCall performs the shared preparation operations between various
// Java method invocation functions.
func setupMethodCall(env interface{}, object interface{}, name string, argSigns []Sign, retSign Sign, args ...interface{}) (jenv *C.JNIEnv, jobject C.jobject, jmid C.jmethodID, jvalArray *C.jvalue, freeFunc func(), err error) {
jenv = getEnv(env)
jobject = getObject(object)
jclass := C.GetObjectClass(jenv, jobject)
var id unsafe.Pointer
id, err = JMethodID(jenv, jclass, name, FuncSign(argSigns, retSign))
if err != nil {
return
}
jmid = C.jmethodID(id)
jvalArray, freeFunc, err = jArgArray(jenv, args, argSigns)
if err != nil {
err = fmt.Errorf("error creating arguments for method %s: %v", name, err)
}
return
}
// setupStaticMethodCall performs the shared preparation operations between
// various Java static method invocation functions.
func setupStaticMethodCall(env interface{}, class interface{}, name string, argSigns []Sign, retSign Sign, args ...interface{}) (jenv *C.JNIEnv, jclass C.jclass, jmid C.jmethodID, jvalArray *C.jvalue, freeFunc func(), err error) {
jenv = getEnv(env)
jclass = getClass(class)
var id unsafe.Pointer
id, err = JStaticMethodID(env, jclass, name, FuncSign(argSigns, retSign))
if err != nil {
return
}
jmid = C.jmethodID(id)
jvalArray, freeFunc, err = jArgArray(jenv, args, argSigns)
if err != nil {
err = fmt.Errorf("error creating arguments for method %s: %v", name, err)
}
return
}
// CallObjectMethod calls a Java method that returns a java object.
func CallObjectMethod(env interface{}, object interface{}, name string, argSigns []Sign, retSign Sign, args ...interface{}) (unsafe.Pointer, error) {
switch retSign {
case ByteSign, CharSign, ShortSign, LongSign, FloatSign, DoubleSign, BoolSign, IntSign, VoidSign:
panic(fmt.Sprintf("Illegal call to CallObjectMethod on method with return sign %s", retSign))
}
jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, retSign, args...)
if err != nil {
return nil, err
}
defer freeFunc()
ret := C.CallObjectMethodA(jenv, jobject, jmid, valArray)
return unsafe.Pointer(ret), JExceptionMsg(env)
}
// CallStringMethod calls a Java method that returns a string.
func CallStringMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (string, error) {
jstr, err := CallObjectMethod(env, object, name, argSigns, StringSign, args...)
return GoString(env, jstr), err
}
// CallByteArrayMethod calls a Java method that returns a byte array.
func CallByteArrayMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) ([]byte, error) {
jArr, err := CallObjectMethod(env, object, name, argSigns, ArraySign(ByteSign), args...)
if err != nil {
return nil, err
}
return GoByteArray(env, jArr), nil
}
// CallObjectArrayMethod calls a Java method that returns an object array.
func CallObjectArrayMethod(env interface{}, object interface{}, name string, argSigns []Sign, retElemSign Sign, args ...interface{}) ([]unsafe.Pointer, error) {
jenv := getEnv(env)
jArr, err := CallObjectMethod(env, object, name, argSigns, ArraySign(retElemSign), args...)
if err != nil {
return nil, err
}
if jArr == nil {
return nil, nil
}
ret := make([]unsafe.Pointer, int(C.GetArrayLength(jenv, C.jarray(jArr))))
for i, _ := range ret {
ret[i] = unsafe.Pointer(C.GetObjectArrayElement(jenv, C.jobjectArray(jArr), C.jsize(i)))
}
return ret, nil
}
// CallStringArrayMethod calls a Java method that returns an string array.
func CallStringArrayMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) ([]string, error) {
objarr, err := CallObjectArrayMethod(env, object, name, argSigns, StringSign, args...)
if err != nil {
return nil, err
}
strs := make([]string, len(objarr))
for i, obj := range objarr {
strs[i] = GoString(env, obj)
}
return strs, nil
}
// CallMapMethod calls a Java method that returns a Map.
func CallMapMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (map[unsafe.Pointer]unsafe.Pointer, error) {
jMap, err := CallObjectMethod(env, object, name, argSigns, MapSign, args...)
if err != nil {
return nil, err
}
if jMap == nil {
return nil, nil
}
jEntrySet, err := CallObjectMethod(env, jMap, "entrySet", nil, SetSign)
if err != nil {
return nil, err
}
jIter, err := CallObjectMethod(env, jEntrySet, "iterator", nil, IteratorSign)
if err != nil {
return nil, err
}
ret := make(map[unsafe.Pointer]unsafe.Pointer)
for {
if hasNext, err := CallBooleanMethod(env, jIter, "hasNext", nil); err != nil {
return nil, err
} else if !hasNext {
break
}
jEntry, err := CallObjectMethod(env, jIter, "next", nil, ObjectSign)
if err != nil {
return nil, err
}
jKey, err := CallObjectMethod(env, jEntry, "getKey", nil, ObjectSign)
if err != nil {
return nil, err
}
jVal, err := CallObjectMethod(env, jEntry, "getValue", nil, ObjectSign)
if err != nil {
return nil, err
}
ret[jKey] = jVal
}
return ret, nil
}
// CallMultimapMethod calls a Java method that returns a Multimap.
func CallMultimapMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (map[unsafe.Pointer][]unsafe.Pointer, error) {
jMultimap, err := CallObjectMethod(env, object, name, argSigns, MultimapSign, args...)
if err != nil {
return nil, err
}
if jMultimap == nil {
return nil, nil
}
jEntrySet, err := CallObjectMethod(env, jMultimap, "entrySet", nil, SetSign)
if err != nil {
return nil, err
}
jIter, err := CallObjectMethod(env, jEntrySet, "iterator", nil, IteratorSign)
if err != nil {
return nil, err
}
ret := make(map[unsafe.Pointer][]unsafe.Pointer)
for {
if hasNext, err := CallBooleanMethod(env, jIter, "hasNext", nil); err != nil {
return nil, err
} else if !hasNext {
break
}
jEntry, err := CallObjectMethod(env, jIter, "next", nil, ObjectSign)
if err != nil {
return nil, err
}
jKey, err := CallObjectMethod(env, jEntry, "getKey", nil, ObjectSign)
if err != nil {
return nil, err
}
jVal, err := CallObjectMethod(env, jEntry, "getValue", nil, ObjectSign)
if err != nil {
return nil, err
}
ret[jKey] = append(ret[jKey], jVal)
}
return ret, nil
}
// CallBooleanMethod calls a Java method that returns a boolean.
func CallBooleanMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (bool, error) {
jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, BoolSign, args...)
if err != nil {
return false, err
}
defer freeFunc()
ret := C.CallBooleanMethodA(jenv, jobject, jmid, valArray) != C.JNI_OK
return ret, JExceptionMsg(env)
}
// CallIntMethod calls a Java method that returns an int.
func CallIntMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (int, error) {
jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, IntSign, args...)
if err != nil {
return 0, err
}
defer freeFunc()
ret := int(C.CallIntMethodA(jenv, jobject, jmid, valArray))
return ret, JExceptionMsg(env)
}
// CallLongMethod calls a Java method that returns an int64.
func CallLongMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) (int64, error) {
jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, LongSign, args...)
if err != nil {
return 0, err
}
defer freeFunc()
ret := int64(C.CallLongMethodA(jenv, jobject, jmid, valArray))
return ret, JExceptionMsg(env)
}
// CallVoidMethod calls a Java method that doesn't return anything.
func CallVoidMethod(env interface{}, object interface{}, name string, argSigns []Sign, args ...interface{}) error {
jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, VoidSign, args...)
if err != nil {
return err
}
defer freeFunc()
C.CallVoidMethodA(jenv, jobject, jmid, valArray)
return JExceptionMsg(env)
}
// CallStaticObjectMethod calls a static Java method that returns a Java object.
func CallStaticObjectMethod(env interface{}, class interface{}, name string, argSigns []Sign, retSign Sign, args ...interface{}) (unsafe.Pointer, error) {
switch retSign {
case ByteSign, CharSign, ShortSign, LongSign, FloatSign, DoubleSign, BoolSign, IntSign, VoidSign:
panic(fmt.Sprintf("Illegal call to CallStaticObjectMethod on method with return sign %s", retSign))
}
jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, retSign, args...)
if err != nil {
return nil, err
}
defer freeFunc()
ret := C.CallStaticObjectMethodA(jenv, jclass, jmid, jvalArray)
return unsafe.Pointer(ret), JExceptionMsg(env)
}
// CallStaticStringMethod calls a static Java method that returns a string.
func CallStaticStringMethod(env interface{}, class interface{}, name string, argSigns []Sign, args ...interface{}) (string, error) {
jString, err := CallStaticObjectMethod(env, class, name, argSigns, StringSign, args...)
return GoString(env, jString), err
}
// CallStaticByteArrayMethod calls a static Java method that returns a byte array.
func CallStaticByteArrayMethod(env interface{}, class interface{}, name string, argSigns []Sign, args ...interface{}) ([]byte, error) {
jArr, err := CallStaticObjectMethod(env, class, name, argSigns, ArraySign(ByteSign), args...)
if err != nil {
return nil, err
}
return GoByteArray(env, jArr), nil
}
// CallStaticLongArrayMethod calls a static Java method that returns a array of long.
func CallStaticLongArrayMethod(env interface{}, class interface{}, name string, argSigns []Sign, args ...interface{}) ([]int64, error) {
jArr, err := CallStaticObjectMethod(env, class, name, argSigns, ArraySign(LongSign), args...)
if err != nil {
return nil, err
}
return GoLongArray(env, jArr), nil
}
// CallStaticIntMethod calls a static Java method that returns an int.
func CallStaticIntMethod(env interface{}, class interface{}, name string, argSigns []Sign, args ...interface{}) (int, error) {
jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, IntSign, args...)
if err != nil {
return 0, err
}
defer freeFunc()
ret := int(C.CallStaticIntMethodA(jenv, jclass, jmid, jvalArray))
return ret, JExceptionMsg(env)
}
// CallStaticVoidMethod calls a static Java method doesn't return anything.
func CallStaticVoidMethod(env interface{}, class interface{}, name string, argSigns []Sign, args ...interface{}) error {
jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, VoidSign, args...)
if err != nil {
return err
}
defer freeFunc()
C.CallStaticVoidMethodA(jenv, jclass, jmid, jvalArray)
return JExceptionMsg(env)
}
func handleError(name string, err error) {
if err != nil {
panic(fmt.Sprintf("error while calling jni method %q: %v", name, err))
}
}
func isSignOneOf(sign Sign, set []Sign) bool {
for _, s := range set {
if sign == s {
return true
}
}
return false
}