v.io/x/jni: support for the Java invoker interface
Change-Id: Idc077cd9cb02776c58c6a9718480862e135c4e00
diff --git a/impl/google/rpc/invoker.go b/impl/google/rpc/invoker.go
index c9d4325..8b4fd10 100644
--- a/impl/google/rpc/invoker.go
+++ b/impl/google/rpc/invoker.go
@@ -9,13 +9,14 @@
import (
"fmt"
"runtime"
+ "unsafe"
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/rpc"
"v.io/v23/vdl"
"v.io/v23/vdlroot/signature"
- "v.io/v23/vom"
+
jchannel "v.io/x/jni/impl/google/channel"
jutil "v.io/x/jni/util"
jcontext "v.io/x/jni/v23/context"
@@ -25,17 +26,25 @@
import "C"
func goInvoker(env *C.JNIEnv, jObj C.jobject) (rpc.Invoker, error) {
- // Create a new Java VDLInvoker object.
- jInvokerObj, err := jutil.NewObject(env, jVDLInvokerClass, []jutil.Sign{jutil.ObjectSign}, jObj)
- if err != nil {
- return nil, fmt.Errorf("error creating Java VDLInvoker object: %v", err)
+ // See if the Java object is an invoker.
+ var jInvoker C.jobject
+ if jutil.IsInstanceOf(env, jObj, jInvokerClass) {
+ jInvoker = jObj
+ } else {
+ // Create a new Java ReflectInvoker object.
+ jReflectInvoker, err := jutil.NewObject(env, jReflectInvokerClass, []jutil.Sign{jutil.ObjectSign}, jObj)
+ if err != nil {
+ return nil, fmt.Errorf("error creating Java ReflectInvoker object: %v", err)
+ }
+ jInvoker = C.jobject(jReflectInvoker)
}
+
// Reference Java invoker; it will be de-referenced when the go invoker
// created below is garbage-collected (through the finalizer callback we
// setup just below).
- jInvoker := C.jobject(jutil.NewGlobalRef(env, jInvokerObj))
+ jInvokerRef := C.jobject(jutil.NewGlobalRef(env, jInvoker))
i := &invoker{
- jInvoker: jInvoker,
+ jInvoker: jInvokerRef,
}
runtime.SetFinalizer(i, func(i *invoker) {
jEnv, freeFunc := jutil.GetEnv()
@@ -81,27 +90,130 @@
if err != nil {
return nil, err
}
-
jStreamServerCall, err := javaStreamServerCall(env, call)
if err != nil {
return nil, err
}
-
- // VOM-encode the input arguments.
- jVomArgs, err := encodeArgs(env, argptrs)
+ // Convert Go arguments to Java.
+ jArgs, err := i.prepareArgs(env, method, argptrs)
if err != nil {
return nil, err
}
// Invoke the method.
- contextSign := jutil.ClassSign("io.v.v23.context.VContext")
- callSign := jutil.ClassSign("io.v.v23.rpc.StreamServerCall")
- replySign := jutil.ClassSign("io.v.impl.google.rpc.VDLInvoker$InvokeReply")
- jReply, err := jutil.CallObjectMethod(env, i.jInvoker, "invoke", []jutil.Sign{contextSign, callSign, jutil.StringSign, jutil.ArraySign(jutil.ArraySign(jutil.ByteSign))}, replySign, jContext, jStreamServerCall, jutil.CamelCase(method), jVomArgs)
+ resultarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "invoke", []jutil.Sign{contextSign, streamServerCallSign, jutil.StringSign, jutil.ArraySign(jutil.ObjectSign)}, jutil.ObjectSign, jContext, jStreamServerCall, jutil.CamelCase(method), jArgs)
if err != nil {
- return nil, fmt.Errorf("error invoking Java method %q: %v", method, err)
+ return nil, err
}
- // Decode and return results.
- return decodeResults(env, C.jobject(jReply))
+ // Convert Java results into Go.
+ return i.prepareResults(env, method, resultarr)
+}
+
+func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {
+ jEnv, freeFunc := jutil.GetEnv()
+ env := (*C.JNIEnv)(jEnv)
+ defer freeFunc()
+
+ jContext, err := jcontext.JavaContext(env, ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ jServerCall, err := JavaServerCall(env, call)
+ if err != nil {
+ return nil, err
+ }
+ ifaceSign := jutil.ClassSign("io.v.v23.vdlroot.signature.Interface")
+ interfacesArr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getSignature", []jutil.Sign{contextSign, serverCallSign}, ifaceSign, jContext, jServerCall)
+ if err != nil {
+ return nil, err
+ }
+
+ result := make([]signature.Interface, len(interfacesArr))
+ for i, jInterface := range interfacesArr {
+ err = jutil.GoVomCopy(jEnv, jInterface, jInterfaceClass, &result[i])
+ if err != nil {
+ return nil, err
+ }
+ }
+ return result, nil
+}
+
+func (i *invoker) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) {
+ jEnv, freeFunc := jutil.GetEnv()
+ env := (*C.JNIEnv)(jEnv)
+ defer freeFunc()
+
+ jContext, err := jcontext.JavaContext(env, ctx, nil)
+ if err != nil {
+ return signature.Method{}, err
+ }
+ jServerCall, err := JavaServerCall(env, call)
+ if err != nil {
+ return signature.Method{}, err
+ }
+ methodSign := jutil.ClassSign("io.v.v23.vdlroot.signature.Method")
+ jMethod, err := jutil.CallObjectMethod(env, i.jInvoker, "getMethodSignature", []jutil.Sign{contextSign, serverCallSign, jutil.StringSign}, methodSign, jContext, jServerCall, method)
+ if err != nil {
+ return signature.Method{}, err
+ }
+
+ var result signature.Method
+ err = jutil.GoVomCopy(jEnv, jMethod, jMethodClass, &result)
+ if err != nil {
+ return signature.Method{}, err
+ }
+ return result, nil
+}
+
+func (i *invoker) Globber() *rpc.GlobState {
+ return &rpc.GlobState{AllGlobber: javaGlobber{i}}
+}
+
+// prepareArgs converts the provided arguments pointers into a Java Object array.
+func (i *invoker) prepareArgs(env *C.JNIEnv, method string, argptrs []interface{}) (unsafe.Pointer, error) {
+ args := make([]interface{}, len(argptrs))
+ for i, argptr := range argptrs {
+ args[i] = interface{}(jutil.DerefOrDie(argptr))
+ }
+ // Get Java argument types.
+ typesarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getArgumentTypes", []jutil.Sign{jutil.StringSign}, jutil.TypeSign, jutil.CamelCase(method))
+ if err != nil {
+ return nil, err
+ }
+ if len(typesarr) != len(args) {
+ return nil, fmt.Errorf("wrong number of arguments for method %s, want %d, have %d", method, len(typesarr), len(args))
+ }
+ arr := make([]interface{}, len(args))
+ for i, arg := range args {
+ var err error
+ if arr[i], err = jutil.JVomCopy(env, arg, typesarr[i]); err != nil {
+ return nil, err
+ }
+ }
+ return jutil.JObjectArray(env, arr, jObjectClass), nil
+}
+
+// prepareResults converts the provided Java result array into a Go slice of *vdl.Value.
+func (i *invoker) prepareResults(env *C.JNIEnv, method string, jResults []unsafe.Pointer) ([]interface{}, error) {
+ // Get Java result types.
+ typesarr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getResultTypes", []jutil.Sign{jutil.StringSign}, jutil.TypeSign, jutil.CamelCase(method))
+ if err != nil {
+ return nil, err
+ }
+ if len(typesarr) != len(jResults) {
+ return nil, fmt.Errorf("wrong number of results for method %s, want %d, have %d", method, len(typesarr), len(jResults))
+ }
+ // VOM-encode Java results and decode into []*vdl.Value.
+ ret := make([]interface{}, len(jResults))
+ for i, jResult := range jResults {
+ data, err := jutil.JVomEncode(env, jResult, typesarr[i])
+ if err != nil {
+ return nil, err
+ }
+ if ret[i], err = jutil.VomDecodeToValue(data); err != nil {
+ return nil, err
+ }
+ }
+ return ret, nil
}
type javaGlobber struct {
@@ -145,7 +257,7 @@
callSign := jutil.ClassSign("io.v.v23.rpc.ServerCall")
channelSign := jutil.ClassSign("io.v.v23.OutputChannel")
- // Invoke the VDLInvoker's glob method.
+ // Calls Java invoker's glob method.
go func(jServerCallRef C.jobject, jOutputChannelRef C.jobject) {
jEnv, freeFunc := jutil.GetEnv()
defer freeFunc()
@@ -157,94 +269,3 @@
return actualChannel, nil
}
-
-func (i *invoker) Globber() *rpc.GlobState {
- return &rpc.GlobState{AllGlobber: javaGlobber{i}}
-}
-
-func (i *invoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {
- jEnv, freeFunc := jutil.GetEnv()
- env := (*C.JNIEnv)(jEnv)
- defer freeFunc()
-
- replySign := jutil.ClassSign("io.v.v23.vdlroot.signature.Interface")
-
- interfacesArr, err := jutil.CallObjectArrayMethod(env, i.jInvoker, "getSignature", nil, replySign)
- if err != nil {
- return nil, err
- }
-
- result := make([]signature.Interface, len(interfacesArr))
- for i, jInterface := range interfacesArr {
- err = jutil.GoVomCopy(jEnv, jInterface, jInterfaceClass, &result[i])
- if err != nil {
- return nil, err
- }
- }
- return result, nil
-}
-
-func (i *invoker) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) {
- jEnv, freeFunc := jutil.GetEnv()
- env := (*C.JNIEnv)(jEnv)
- defer freeFunc()
-
- replySign := jutil.ClassSign("io.v.v23.vdlroot.signature.Method")
-
- jMethod, err := jutil.CallObjectMethod(env, i.jInvoker, "getMethodSignature", []jutil.Sign{jutil.StringSign}, replySign, method)
- if err != nil {
- return signature.Method{}, err
- }
-
- var result signature.Method
- err = jutil.GoVomCopy(jEnv, jMethod, jMethodClass, &result)
- if err != nil {
- return signature.Method{}, err
- }
- return result, nil
-}
-
-// encodeArgs VOM-encodes the provided arguments pointers and returns them as a
-// Java array of byte arrays.
-func encodeArgs(env *C.JNIEnv, argptrs []interface{}) (C.jobjectArray, error) {
- vomArgs := make([][]byte, len(argptrs))
- for i, argptr := range argptrs {
- arg := interface{}(jutil.DerefOrDie(argptr))
- var err error
- if vomArgs[i], err = vom.Encode(arg); err != nil {
- return nil, err
- }
- }
- return C.jobjectArray(jutil.JByteArrayArray(env, vomArgs)), nil
-}
-
-// decodeResults VOM-decodes replies stored in the Java reply object into
-// an array *vdl.Value.
-func decodeResults(env *C.JNIEnv, jReply C.jobject) ([]interface{}, error) {
- // Unpack the replies.
- results, err := jutil.JByteArrayArrayField(env, jReply, "results")
- if err != nil {
- return nil, err
- }
- vomAppErr, err := jutil.JByteArrayField(env, jReply, "vomAppError")
- if err != nil {
- return nil, err
- }
- // Check for app error.
- if vomAppErr != nil {
- var appErr error
- if err := vom.Decode(vomAppErr, &appErr); err != nil {
- return nil, err
- }
- return nil, appErr
- }
- // VOM-decode results into *vdl.Value instances.
- ret := make([]interface{}, len(results))
- for i, result := range results {
- var err error
- if ret[i], err = jutil.VomDecodeToValue(result); err != nil {
- return nil, err
- }
- }
- return ret, nil
-}
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index 248e741..c4ec851 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -26,8 +26,12 @@
import "C"
var (
- optionsSign = jutil.ClassSign("io.v.v23.Options")
- streamSign = jutil.ClassSign("io.v.impl.google.rpc.Stream")
+ contextSign = jutil.ClassSign("io.v.v23.context.VContext")
+ serverCallSign = jutil.ClassSign("io.v.v23.rpc.ServerCall")
+ streamServerCallSign = jutil.ClassSign("io.v.v23.rpc.StreamServerCall")
+ streamSign = jutil.ClassSign("io.v.impl.google.rpc.Stream")
+ optionsSign = jutil.ClassSign("io.v.v23.Options")
+
listenAddrSign = jutil.ClassSign("io.v.v23.rpc.ListenSpec$Address")
addressChooserSign = jutil.ClassSign("io.v.v23.rpc.AddressChooser")
serverStateSign = jutil.ClassSign("io.v.v23.rpc.ServerState")
@@ -45,24 +49,26 @@
jServerCallClass C.jclass
// Global reference for io.v.impl.google.rpc.Stream class.
jStreamClass C.jclass
- // Global reference for io.v.impl.google.rpc.VDLInvoker class.
- jVDLInvokerClass C.jclass
- // Global reference for io.v.v23.rpc.ServerStatus class.
- jServerStatusClass C.jclass
- // Global reference for io.v.v23.rpc.ServerState class.
- jServerStateClass C.jclass
- // Global reference for io.v.v23.rpc.MountStatus class.
- jMountStatusClass C.jclass
- // Global reference for io.v.v23.rpc.ProxyStatus class.
- jProxyStatusClass C.jclass
+ // Global reference for io.v.v23.rpc.Invoker class.
+ jInvokerClass C.jclass
// Global reference for io.v.v23.rpc.ListenSpec class.
jListenSpecClass C.jclass
// Global reference for io.v.v23.rpc.ListenSpec$Address class.
jListenSpecAddressClass C.jclass
+ // Global reference for io.v.v23.rpc.MountStatus class.
+ jMountStatusClass C.jclass
// Global reference for io.v.v23.rpc.NetworkAddress class.
jNetworkAddressClass C.jclass
// Global reference for io.v.v23.rpc.NetworkChange class.
jNetworkChangeClass C.jclass
+ // Global reference for io.v.v23.rpc.ProxyStatus class.
+ jProxyStatusClass C.jclass
+ // Global reference for io.v.v23.rpc.ReflectInvoker class.
+ jReflectInvokerClass C.jclass
+ // Global reference for io.v.v23.rpc.ServerStatus class.
+ jServerStatusClass C.jclass
+ // Global reference for io.v.v23.rpc.ServerState class.
+ jServerStateClass C.jclass
// Global reference for io.v.v23.OptionDefs class.
jOptionDefsClass C.jclass
// Global reference for java.io.EOFException class.
@@ -75,6 +81,8 @@
jMethodClass C.jclass
// Global reference for io.v.v23.naming.GlobReply
jGlobReplyClass C.jclass
+ // Global reference for java.lang.Object class.
+ jObjectClass C.jclass
)
// Init initializes the JNI code with the given Java environment. This method
@@ -121,31 +129,11 @@
return err
}
jStreamClass = C.jclass(class)
- class, err = jutil.JFindClass(jEnv, "io/v/impl/google/rpc/VDLInvoker")
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/Invoker")
if err != nil {
return err
}
- jVDLInvokerClass = C.jclass(class)
- class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ServerStatus")
- if err != nil {
- return err
- }
- jServerStatusClass = C.jclass(class)
- class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ServerState")
- if err != nil {
- return err
- }
- jServerStateClass = C.jclass(class)
- class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/MountStatus")
- if err != nil {
- return err
- }
- jMountStatusClass = C.jclass(class)
- class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ProxyStatus")
- if err != nil {
- return err
- }
- jProxyStatusClass = C.jclass(class)
+ jInvokerClass = C.jclass(class)
class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ListenSpec")
if err != nil {
return err
@@ -156,6 +144,11 @@
return err
}
jListenSpecAddressClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/MountStatus")
+ if err != nil {
+ return err
+ }
+ jMountStatusClass = C.jclass(class)
class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/NetworkAddress")
if err != nil {
return err
@@ -166,6 +159,26 @@
return err
}
jNetworkChangeClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ProxyStatus")
+ if err != nil {
+ return err
+ }
+ jProxyStatusClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ReflectInvoker")
+ if err != nil {
+ return err
+ }
+ jReflectInvokerClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ServerStatus")
+ if err != nil {
+ return err
+ }
+ jServerStatusClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "io/v/v23/rpc/ServerState")
+ if err != nil {
+ return err
+ }
+ jServerStateClass = C.jclass(class)
class, err = jutil.JFindClass(jEnv, "io/v/v23/OptionDefs")
if err != nil {
return err
@@ -196,6 +209,11 @@
return err
}
jGlobReplyClass = C.jclass(class)
+ class, err = jutil.JFindClass(jEnv, "java/lang/Object")
+ if err != nil {
+ return err
+ }
+ jObjectClass = C.jclass(class)
return nil
}
diff --git a/test/fortune/fortune.vdl b/test/fortune/fortune.vdl
index 80b7bb9..4ce94c5 100644
--- a/test/fortune/fortune.vdl
+++ b/test/fortune/fortune.vdl
@@ -32,6 +32,9 @@
// StreamingGet returns a stream that can be used to obtain fortunes.
StreamingGet() stream<bool, string> (total int32 | error) {access.Read}
+ // MultipleGet returns the same fortune twice.
+ MultipleGet() (Fortune string, Another string | error) {access.Read}
+
// GetComplexError returns (always!) ErrComplex.
GetComplexError() error {access.Read}
diff --git a/test/fortune/fortune.vdl.go b/test/fortune/fortune.vdl.go
index dfe4a92..e56e09c 100644
--- a/test/fortune/fortune.vdl.go
+++ b/test/fortune/fortune.vdl.go
@@ -67,6 +67,8 @@
Get(*context.T, ...rpc.CallOpt) (Fortune string, err error)
// StreamingGet returns a stream that can be used to obtain fortunes.
StreamingGet(*context.T, ...rpc.CallOpt) (FortuneStreamingGetClientCall, error)
+ // MultipleGet returns the same fortune twice.
+ MultipleGet(*context.T, ...rpc.CallOpt) (Fortune string, Another string, err error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, ...rpc.CallOpt) error
// NoTags is a method without tags.
@@ -110,6 +112,11 @@
return
}
+func (c implFortuneClientStub) MultipleGet(ctx *context.T, opts ...rpc.CallOpt) (o0 string, o1 string, err error) {
+ err = v23.GetClient(ctx).Call(ctx, c.name, "MultipleGet", nil, []interface{}{&o0, &o1}, opts...)
+ return
+}
+
func (c implFortuneClientStub) GetComplexError(ctx *context.T, opts ...rpc.CallOpt) (err error) {
err = v23.GetClient(ctx).Call(ctx, c.name, "GetComplexError", nil, nil, opts...)
return
@@ -238,6 +245,8 @@
Get(*context.T, rpc.ServerCall) (Fortune string, err error)
// StreamingGet returns a stream that can be used to obtain fortunes.
StreamingGet(*context.T, FortuneStreamingGetServerCall) (total int32, err error)
+ // MultipleGet returns the same fortune twice.
+ MultipleGet(*context.T, rpc.ServerCall) (Fortune string, Another string, err error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, rpc.ServerCall) error
// NoTags is a method without tags.
@@ -258,6 +267,8 @@
Get(*context.T, rpc.ServerCall) (Fortune string, err error)
// StreamingGet returns a stream that can be used to obtain fortunes.
StreamingGet(*context.T, *FortuneStreamingGetServerCallStub) (total int32, err error)
+ // MultipleGet returns the same fortune twice.
+ MultipleGet(*context.T, rpc.ServerCall) (Fortune string, Another string, err error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, rpc.ServerCall) error
// NoTags is a method without tags.
@@ -308,6 +319,10 @@
return s.impl.StreamingGet(ctx, call)
}
+func (s implFortuneServerStub) MultipleGet(ctx *context.T, call rpc.ServerCall) (string, string, error) {
+ return s.impl.MultipleGet(ctx, call)
+}
+
func (s implFortuneServerStub) GetComplexError(ctx *context.T, call rpc.ServerCall) error {
return s.impl.GetComplexError(ctx, call)
}
@@ -362,6 +377,15 @@
Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
},
{
+ Name: "MultipleGet",
+ Doc: "// MultipleGet returns the same fortune twice.",
+ OutArgs: []rpc.ArgDesc{
+ {"Fortune", ``}, // string
+ {"Another", ``}, // string
+ },
+ Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
+ },
+ {
Name: "GetComplexError",
Doc: "// GetComplexError returns (always!) ErrComplex.",
Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
diff --git a/util/call.go b/util/call.go
index faa63f0..db66800 100644
--- a/util/call.go
+++ b/util/call.go
@@ -183,7 +183,7 @@
// 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) {
+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)
@@ -200,7 +200,7 @@
// 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) {
+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)
@@ -220,7 +220,7 @@
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)
+ jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, retSign, args...)
if err != nil {
return nil, err
}
@@ -358,7 +358,7 @@
// 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)
+ jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, BoolSign, args...)
if err != nil {
return false, err
}
@@ -369,7 +369,7 @@
// 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)
+ jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, IntSign, args...)
if err != nil {
return 0, err
}
@@ -380,7 +380,7 @@
// 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)
+ jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, LongSign, args...)
if err != nil {
return 0, err
}
@@ -391,7 +391,7 @@
// 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)
+ jenv, jobject, jmid, valArray, freeFunc, err := setupMethodCall(env, object, name, argSigns, VoidSign, args...)
if err != nil {
return err
}
@@ -406,7 +406,7 @@
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)
+ jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, retSign, args...)
if err != nil {
return nil, err
}
@@ -432,7 +432,7 @@
// 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)
+ jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, IntSign, args...)
if err != nil {
return 0, err
}
@@ -443,7 +443,7 @@
// 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)
+ jenv, jclass, jmid, jvalArray, freeFunc, err := setupStaticMethodCall(env, class, name, argSigns, VoidSign, args...)
if err != nil {
return err
}
diff --git a/util/coding.go b/util/coding.go
index 81b6bcc..89276ff 100644
--- a/util/coding.go
+++ b/util/coding.go
@@ -41,12 +41,12 @@
return vom.Decode(data, dstptr)
}
-// JVomEncode VOM-encodes the provided Java object of the given class.
+// JVomEncode VOM-encodes the provided Java object of the given type.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
-func JVomEncode(jEnv, jObj, jClass interface{}) ([]byte, error) {
- return CallStaticByteArrayMethod(jEnv, jVomUtilClass, "encode", []Sign{ObjectSign, TypeSign}, jObj, jClass)
+func JVomEncode(jEnv, jObj, jType interface{}) ([]byte, error) {
+ return CallStaticByteArrayMethod(jEnv, jVomUtilClass, "encode", []Sign{ObjectSign, TypeSign}, jObj, jType)
}
// JVomEncode VOM-encodes the provided Java VdlValue object.
@@ -61,12 +61,12 @@
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
-func JVomDecode(jEnv interface{}, data []byte, jClass interface{}) (unsafe.Pointer, error) {
- class := getClass(jClass)
- if class == nil {
- class = jObjectClass
+func JVomDecode(jEnv interface{}, data []byte, jTypeObj interface{}) (unsafe.Pointer, error) {
+ jType := getObject(jTypeObj)
+ if jType == nil {
+ jType = C.jobject(jObjectClass)
}
- return CallStaticObjectMethod(jEnv, jVomUtilClass, "decode", []Sign{ByteArraySign, TypeSign}, ObjectSign, data, class)
+ return CallStaticObjectMethod(jEnv, jVomUtilClass, "decode", []Sign{ByteArraySign, TypeSign}, ObjectSign, data, jType)
}
// JVomCopy copies the provided Go value into a corresponding Java object by
@@ -74,12 +74,12 @@
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
-func JVomCopy(jEnv interface{}, src interface{}, jClass interface{}) (unsafe.Pointer, error) {
- data, err := vom.Encode(src)
+func JVomCopy(jEnv interface{}, val interface{}, jType interface{}) (unsafe.Pointer, error) {
+ data, err := vom.Encode(val)
if err != nil {
return nil, err
}
- return JVomDecode(jEnv, data, jClass)
+ return JVomDecode(jEnv, data, jType)
}
// GoVomCopy copies the provided Java object into a provided Go value pointer by
diff --git a/util/util.go b/util/util.go
index 0c313d7..9f470e5 100644
--- a/util/util.go
+++ b/util/util.go
@@ -280,15 +280,11 @@
if err == nil {
return nil, nil
}
- data, err := vom.Encode(err)
- if err != nil {
- return nil, err
- }
- return JVomDecode(jEnv, data, jVExceptionClass)
+ return JVomCopy(jEnv, err, jVExceptionClass)
}
-// JExceptionMsg returns the exception message if an exception occurred, or
-// nil otherwise.
+// JExceptionMsg returns the exception message as a Go error, if an exception
+// occurred, or nil otherwise.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
@@ -299,11 +295,40 @@
return nil
}
C.ExceptionClear(env)
- id, err := JMethodID(env, jThrowableClass, "getMessage", FuncSign(nil, StringSign))
- if err != nil {
- panic(err.Error())
+ if IsInstanceOf(jEnv, C.jobject(e), jVExceptionClass) {
+ // VException: convert it into a verror.
+ // Note that we can't use CallStaticObjectMethod below as it may lead to
+ // an infinite loop.
+ jenv, jclass, jmid, jValArray, freeFunc, err := setupStaticMethodCall(env, jVomUtilClass, "encode", []Sign{ObjectSign, TypeSign}, ByteArraySign, C.jobject(e), jVExceptionClass)
+ if err != nil {
+ return fmt.Errorf("error converting VException: " + err.Error())
+ }
+ defer freeFunc()
+ jData := C.CallStaticObjectMethodA(jenv, jclass, jmid, jValArray)
+ if e := C.ExceptionOccurred(env); e != nil {
+ C.ExceptionClear(env)
+ return fmt.Errorf("error converting VException: exception during VomUtil.encode()")
+ }
+ data := GoByteArray(env, jData)
+ var verr error
+ if err := vom.Decode(data, &verr); err != nil {
+ return fmt.Errorf("error converting VException: " + err.Error())
+ }
+ return verr
}
- jMsg := C.CallGetExceptionMessage(env, C.jobject(e), C.jmethodID(id))
+ // Not a VException: convert it into a Go error.
+ // Note that we can't use CallObjectMethod below, as it may lead to an
+ // infinite loop.
+ jenv, jobject, jmid, jValArray, freeFunc, err := setupMethodCall(env, C.jobject(e), "getMessage", nil, StringSign)
+ if err != nil {
+ return fmt.Errorf("error converting exception: " + err.Error())
+ }
+ defer freeFunc()
+ jMsg := C.CallObjectMethodA(jenv, jobject, jmid, jValArray)
+ if e := C.ExceptionOccurred(env); e != nil {
+ C.ExceptionClear(env)
+ return fmt.Errorf("error converting exception: exception during Throwable.getMessage()")
+ }
return errors.New(GoString(env, jMsg))
}