Merge "fix unnecessary (and erroneous) use of jutil.JString"
diff --git a/impl/google/channel/jni.go b/impl/google/channel/jni.go
index 4daff97..81ede12 100644
--- a/impl/google/channel/jni.go
+++ b/impl/google/channel/jni.go
@@ -7,10 +7,8 @@
package channel
import (
- "fmt"
"unsafe"
- "v.io/v23/verror"
jutil "v.io/x/jni/util"
)
@@ -18,12 +16,10 @@
import "C"
var (
- // Global reference for io.v.impl.google.channel.ChannelIterable class.
- jChannelIterableClass jutil.Class
+ // Global reference for io.v.impl.google.channel.InputChannelImpl class.
+ jInputChannelImplClass jutil.Class
// Global reference for io.v.impl.google.channel.OutputChannelImpl class.
jOutputChannelImplClass jutil.Class
- // Global reference for java.io.EOFException class.
- jEOFExceptionClass jutil.Class
)
// Init initializes the JNI code with the given Java environment. This method
@@ -31,7 +27,7 @@
// from the main Java thread (e.g., On_Load()).
func Init(env jutil.Env) error {
var err error
- jChannelIterableClass, err = jutil.JFindClass(env, "io/v/impl/google/channel/ChannelIterable")
+ jInputChannelImplClass, err = jutil.JFindClass(env, "io/v/impl/google/channel/InputChannelImpl")
if err != nil {
return err
}
@@ -39,63 +35,54 @@
if err != nil {
return err
}
- jEOFExceptionClass, err = jutil.JFindClass(env, "java/io/EOFException")
+ return nil
+}
+
+//export Java_io_v_impl_google_channel_InputChannelImpl_nativeRecv
+func Java_io_v_impl_google_channel_InputChannelImpl_nativeRecv(jenv *C.JNIEnv, jInputChannelImpl C.jobject, goRecvPtr C.jlong, jCallbackObj C.jobject) {
+ env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ recv := *(*func() (jutil.Object, error))(jutil.NativePtr(goRecvPtr))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+ jutil.DoAsyncCall(env, jCallback, recv)
+}
+
+//export Java_io_v_impl_google_channel_InputChannelImpl_nativeFinalize
+func Java_io_v_impl_google_channel_InputChannelImpl_nativeFinalize(jenv *C.JNIEnv, jInputChannelImpl C.jobject, goRecvPtr C.jlong) {
+ jutil.GoUnref(jutil.NativePtr(goRecvPtr))
+}
+
+//export Java_io_v_impl_google_channel_OutputChannelImpl_nativeSend
+func Java_io_v_impl_google_channel_OutputChannelImpl_nativeSend(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goConvertPtr C.jlong, goSendPtr C.jlong, jItemObj C.jobject, jCallbackObj C.jobject) {
+ env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ convert := *(*func(jutil.Object) (interface{}, error))(jutil.NativePtr(goConvertPtr))
+ send := *(*func(interface{}) error)(jutil.NativePtr(goSendPtr))
+ jItem := jutil.Object(uintptr(unsafe.Pointer(jItemObj)))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+ // NOTE(spetrovic): Conversion must be done outside of DoAsyncCall as it references a Java
+ // object.
+ item, err := convert(jItem)
if err != nil {
- return err
+ jutil.CallbackOnFailure(env, jCallback, err)
+ return
}
- return nil
-}
-
-//export Java_io_v_impl_google_channel_ChannelIterable_nativeReadValue
-func Java_io_v_impl_google_channel_ChannelIterable_nativeReadValue(jenv *C.JNIEnv, jChannelIterable C.jobject, goChanPtr C.jlong, goConvertPtr C.jlong) C.jobject {
- env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
- c := *(*chan interface{})(jutil.NativePtr(goChanPtr))
- convert := *(*func(jutil.Env, interface{})(jutil.Object, error))(jutil.NativePtr(goConvertPtr))
- val, ok := <-c
- if !ok { // channel closed
- jutil.JThrow(env, jEOFExceptionClass, "Reached end of input channel.")
- return nil
- }
- jVal, err := convert(env, val)
- if err == nil {
- return C.jobject(unsafe.Pointer(jVal))
- }
- if verr, ok := err.(verror.E); ok && verr.ID == verror.ErrCanceled.ID { // EOF
- jutil.JThrow(env, jEOFExceptionClass, "User canceled the operation.")
- return nil
- }
- jutil.JThrowV(env, err)
- return nil
-}
-
-//export Java_io_v_impl_google_channel_ChannelIterable_nativeFinalize
-func Java_io_v_impl_google_channel_ChannelIterable_nativeFinalize(jenv *C.JNIEnv, jChannelIterable C.jobject, goChanPtr C.jlong, goConvertPtr C.jlong) {
- jutil.GoUnref(jutil.NativePtr(goChanPtr))
- jutil.GoUnref(jutil.NativePtr(goConvertPtr))
-}
-
-//export Java_io_v_impl_google_channel_OutputChannelImpl_nativeWriteValue
-func Java_io_v_impl_google_channel_OutputChannelImpl_nativeWriteValue(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goChanPtr C.jlong, jObject C.jobject) {
- env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
- outCh := *(*outputChannel)(jutil.NativePtr(goChanPtr))
- // The other side of the channel is responsible for deleting this
- // global reference.
- if err := outCh.ReadFunc(jutil.NewGlobalRef(env, jutil.Object(uintptr(unsafe.Pointer(jObject))))); err != nil {
- jutil.JThrowV(env, fmt.Errorf("Exception while writing to OutputChannel: %+v", err))
- }
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return jutil.NullObject, send(item)
+ })
}
//export Java_io_v_impl_google_channel_OutputChannelImpl_nativeClose
-func Java_io_v_impl_google_channel_OutputChannelImpl_nativeClose(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goChanPtr C.jlong) {
+func Java_io_v_impl_google_channel_OutputChannelImpl_nativeClose(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goClosePtr C.jlong, jCallbackObj C.jobject) {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
- outCh := *(*outputChannel)(jutil.NativePtr(goChanPtr))
-
- if err := outCh.CloseFunc(); err != nil {
- jutil.JThrowV(env, fmt.Errorf("Exception while closing OutputChannel: %+v", err))
- }
+ close := *(*func() error)(jutil.NativePtr(goClosePtr))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return jutil.NullObject, close()
+ })
}
//export Java_io_v_impl_google_channel_OutputChannelImpl_nativeFinalize
-func Java_io_v_impl_google_channel_OutputChannelImpl_nativeFinalize(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goChanPtr C.jlong) {
- jutil.GoUnref(jutil.NativePtr(goChanPtr))
+func Java_io_v_impl_google_channel_OutputChannelImpl_nativeFinalize(jenv *C.JNIEnv, jOutputChannelClass C.jclass, goConvertPtr C.jlong, goSendPtr C.jlong, goClosePtr C.jlong) {
+ jutil.GoUnref(jutil.NativePtr(goConvertPtr))
+ jutil.GoUnref(jutil.NativePtr(goSendPtr))
+ jutil.GoUnref(jutil.NativePtr(goClosePtr))
}
diff --git a/impl/google/channel/util.go b/impl/google/channel/util.go
index 331d542..486307a 100644
--- a/impl/google/channel/util.go
+++ b/impl/google/channel/util.go
@@ -13,54 +13,30 @@
// #include "jni.h"
import "C"
-// JavaIterable converts the provided Go channel into a Java VIterable object, using the
-// given convert function.
-func JavaIterable(env jutil.Env, ch chan interface{}, convert func(jutil.Env, interface{}) (jutil.Object, error)) (jutil.Object, error) {
- jIterable, err := jutil.NewObject(env, jChannelIterableClass, []jutil.Sign{jutil.LongSign, jutil.LongSign}, int64(jutil.PtrValue(&ch)), int64(jutil.PtrValue(&convert)))
+// JavaInputChannel creates a new Java InputChannel object given the provided Go recv function.
+//
+// All objects returned by the recv function must be globally references.
+//
+// The recv function must return verror.ErrEndOfFile when there are no more elements
+// to receive.
+func JavaInputChannel(env jutil.Env, recv func() (jutil.Object, error)) (jutil.Object, error) {
+ jInputChannel, err := jutil.NewObject(env, jInputChannelImplClass, []jutil.Sign{jutil.LongSign}, int64(jutil.PtrValue(&recv)))
if err != nil {
return jutil.NullObject, err
}
- jutil.GoRef(&ch) // Un-refed when ChannelIterable is finalized.
- jutil.GoRef(&convert) // Un-refed when ChannelIterable is finalized.
- return jIterable, nil
+ jutil.GoRef(&recv) // Un-refed when jInputChannel is finalized.
+ return jInputChannel, nil
}
-// outputChannel represents the Go-side of a Java OutputChannel. Each time the
-// Java side writes an object, the ReadFunc will be called. If the ReadFunc
-// returns an error, an exception will be raised on the Java side.
-type outputChannel struct {
- // ReadFunc is invoked when an object has been read from Java.
- //
- // The jutil.Object passed to this function is globally referenced. It is
- // required that the ReadFunc implementation delete the global
- // reference jutil.DeleteGlobalRef. Failure to do so will
- // result in a reference leak.
- //
- // If the ReadFunc implementation returns an error, that error will be
- // passed back to the Java writer in the form of a VException to the
- // OutputChannel.writeValue() method.
- ReadFunc func(jutil.Object) error
-
- // CloseFunc is invoked when the Java side has closed its OutputChannel
- // and no more values will be written.
- //
- // If CloseFunc returns an error, that error will be passed back to the
- // Java side in the form of a VException OutputChannel.close() method.
- CloseFunc func() error
-}
-
-// JavaOutputChannel converts the provided Go channel of jutil.Object values
-// into a Java OutputChannel object.
-func JavaOutputChannel(env jutil.Env, readFunc func(jutil.Object) error, closeFunc func() error) (jutil.Object, error) {
- outCh := outputChannel{
- ReadFunc: readFunc,
- CloseFunc: closeFunc,
- }
-
- jOutputChannel, err := jutil.NewObject(env, jOutputChannelImplClass, []jutil.Sign{jutil.LongSign}, int64(jutil.PtrValue(&outCh)))
+// JavaOutputChannel creates a new Java OutputChannel object given the provided Go convert, send
+// and close functions. Send is invoked with the result of convert, which must be non-blocking.
+func JavaOutputChannel(env jutil.Env, convert func(jutil.Object) (interface{}, error), send func(interface{}) error, close func() error) (jutil.Object, error) {
+ jOutputChannel, err := jutil.NewObject(env, jOutputChannelImplClass, []jutil.Sign{jutil.LongSign, jutil.LongSign, jutil.LongSign}, int64(jutil.PtrValue(&convert)), int64(jutil.PtrValue(&send)), int64(jutil.PtrValue(&close)))
if err != nil {
return jutil.NullObject, err
}
- jutil.GoRef(&outCh) // Un-refed when the OutputChannel is finalized.
+ jutil.GoRef(&convert) // Un-refed when jOutputChannel is finalized.
+ jutil.GoRef(&send) // Un-refed when jOutputChannel is finalized.
+ jutil.GoRef(&close) // Un-refed when jOutputChannel is finalized.
return jOutputChannel, nil
}
diff --git a/impl/google/namespace/jni.go b/impl/google/namespace/jni.go
index 96a6035..35c9481 100644
--- a/impl/google/namespace/jni.go
+++ b/impl/google/namespace/jni.go
@@ -71,31 +71,39 @@
return
}
-func doGlob(env jutil.Env, n namespace.T, context *context.T, pattern string, opts []naming.NamespaceOpt) (jutil.Object, error) {
+func doGlob(n namespace.T, context *context.T, pattern string, opts []naming.NamespaceOpt) (jutil.Object, error) {
c, err := n.Glob(context, pattern, opts...)
if err != nil {
return jutil.NullObject, err
}
- valChan := make(chan interface{}, 100)
- go func() {
- for val := range c {
- valChan <- val
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jChannel, err := jchannel.JavaInputChannel(env, func() (jutil.Object, error) {
+ // This is a blocking call, so don't call GetEnv() before this line.
+ val, ok := <-c
+ if !ok {
+ return jutil.NullObject, verror.NewErrEndOfFile(context)
}
- close(valChan)
- }()
- return jchannel.JavaIterable(env, valChan, func(env jutil.Env, val interface{}) (jutil.Object, error) {
globReply, ok := val.(naming.GlobReply)
if !ok {
return jutil.NullObject, fmt.Errorf("Expected value of GlobReply type, got type %T: %v", val, val)
}
- // Check for a canceled context error, we surface these as EOF.
- if errorEntry, ok := globReply.(*naming.GlobReplyError); ok {
- if verr, ok := errorEntry.Value.Error.(verror.E); ok && verr.ID == verror.ErrCanceled.ID {
- return jutil.NullObject, verr
- }
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jReply, err := jutil.JVomCopy(env, globReply, jGlobReplyClass)
+ if err != nil {
+ return jutil.NullObject, err
}
- return jutil.JVomCopy(env, val, jGlobReplyClass)
+ // Must grab a global reference as we free up the env and all local references that come
+ // along with it.
+ return jutil.NewGlobalRef(env, jReply), nil // Un-refed by InputChannelImpl_nativeRecv
})
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jChannel), nil // Un-refed by DoAsyncCall
}
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGlob
@@ -108,8 +116,8 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doGlob(env, n, context, pattern, opts)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doGlob(n, context, pattern, opts)
})
}
@@ -138,7 +146,7 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
return jutil.NullObject, n.Mount(context, name, server, duration, options...)
})
}
@@ -164,7 +172,7 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
return jutil.NullObject, n.Unmount(context, name, server, options...)
})
}
@@ -193,7 +201,7 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
return jutil.NullObject, n.Delete(context, name, deleteSubtree, options...)
})
}
@@ -211,12 +219,20 @@
return
}
-func doResolve(env jutil.Env, n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
+func doResolve(n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
entry, err := n.Resolve(context, name, options...)
if err != nil {
return jutil.NullObject, err
}
- return jutil.JVomCopy(env, entry, jMountEntryClass)
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jEntry, err := jutil.JVomCopy(env, entry, jMountEntryClass)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jEntry), nil // Un-refed in DoAsyncCall
}
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeResolve
@@ -229,8 +245,8 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doResolve(env, n, context, name, options)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doResolve(n, context, name, options)
})
}
@@ -247,12 +263,20 @@
return
}
-func doResolveToMountTable(env jutil.Env, n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
+func doResolveToMountTable(n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
entry, err := n.ResolveToMountTable(context, name, options...)
if err != nil {
return jutil.NullObject, err
}
- return jutil.JVomCopy(env, entry, jMountEntryClass)
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jEntry, err := jutil.JVomCopy(env, entry, jMountEntryClass)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jEntry), nil // Un-refed in DoAsyncCall
}
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeResolveToMountTable
@@ -265,8 +289,8 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doResolveToMountTable(env, n, context, name, options)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doResolveToMountTable(n, context, name, options)
})
}
@@ -327,7 +351,7 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
return jutil.NullObject, n.SetPermissions(context, name, permissions, version, options...)
})
}
@@ -345,18 +369,26 @@
return
}
-func doGetPermissions(env jutil.Env, n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
+func doGetPermissions(n namespace.T, context *context.T, name string, options []naming.NamespaceOpt) (jutil.Object, error) {
permissions, version, err := n.GetPermissions(context, name, options...)
if err != nil {
return jutil.NullObject, err
}
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
jPermissions, err := jutil.JVomCopy(env, permissions, jPermissionsClass)
if err != nil {
return jutil.NullObject, err
}
result := make(map[jutil.Object]jutil.Object)
result[jutil.JString(env, version)] = jPermissions
- return jutil.JObjectMap(env, result)
+ jResult, err := jutil.JObjectMap(env, result)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jResult), nil // Un-refed in DoAsyncCall
}
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGetPermissions
@@ -369,8 +401,8 @@
jutil.CallbackOnFailure(env, jCallback, err)
return
}
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doGetPermissions(env, n, context, name, options)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doGetPermissions(n, context, name, options)
})
}
diff --git a/impl/google/rpc/invoker.go b/impl/google/rpc/invoker.go
index 28449b4..7dc85a6 100644
--- a/impl/google/rpc/invoker.go
+++ b/impl/google/rpc/invoker.go
@@ -201,24 +201,26 @@
return err
}
- readFunc := func(input jutil.Object) error {
+ convert := func(input jutil.Object) (interface{}, error) {
env, freeFunc := jutil.GetEnv()
- defer jutil.DeleteGlobalRef(env, input)
+ defer freeFunc()
var reply naming.GlobReply
- err := jutil.GoVomCopy(env, input, jGlobReplyClass, &reply)
- if err != nil {
- freeFunc()
- return err
+ if err := jutil.GoVomCopy(env, input, jGlobReplyClass, &reply); err != nil {
+ return nil, err
}
- freeFunc()
- call.SendStream().Send(reply)
+ return reply, nil
+ }
+ send := func(item interface{}) error {
+ reply, ok := item.(naming.GlobReply)
+ if !ok {
+ return fmt.Errorf("Expected item of type naming.GlobReply, got: %T", reply)
+ }
+ return call.SendStream().Send(reply)
+ }
+ close := func() error {
return nil
}
- closeFunc := func() error {
- return nil
- }
-
- jOutputChannel, err := jchannel.JavaOutputChannel(env, readFunc, closeFunc)
+ jOutputChannel, err := jchannel.JavaOutputChannel(env, convert, send, close)
if err != nil {
return err
}
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index be7af99..b643d86 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -22,6 +22,7 @@
jcontext "v.io/x/jni/v23/context"
jnaming "v.io/x/jni/v23/naming"
jsecurity "v.io/x/jni/v23/security"
+ "v.io/v23/verror"
)
// #include "jni.h"
@@ -74,8 +75,6 @@
jServerStateClass jutil.Class
// Global reference for io.v.v23.OptionDefs class.
jOptionDefsClass jutil.Class
- // Global reference for java.io.EOFException class.
- jEOFExceptionClass jutil.Class
// Global reference for io.v.v23.naming.Endpoint.
jEndpointClass jutil.Class
// Global reference for io.v.v23.vdlroot.signature.Interface class.
@@ -170,10 +169,6 @@
if err != nil {
return err
}
- jEOFExceptionClass, err = jutil.JFindClass(env, "java/io/EOFException")
- if err != nil {
- return err
- }
jEndpointClass, err = jutil.JFindClass(env, "io/v/v23/naming/Endpoint")
if err != nil {
return err
@@ -257,7 +252,7 @@
return args, nil
}
-func doStartCall(env jutil.Env, context *context.T, name, method string, skipServerAuth bool, goPtr C.jlong, args []interface{}) (jutil.Object, error) {
+func doStartCall(context *context.T, name, method string, skipServerAuth bool, goPtr C.jlong, args []interface{}) (jutil.Object, error) {
var opts []rpc.CallOpt
if skipServerAuth {
opts = append(opts,
@@ -269,11 +264,15 @@
if err != nil {
return jutil.NullObject, err
}
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
jCall, err := javaCall(env, call)
if err != nil {
return jutil.NullObject, err
}
- return jCall, nil
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jCall), nil // Un-refed in DoAsyncCall
}
//export Java_io_v_impl_google_rpc_ClientImpl_nativeStartCall
@@ -293,8 +292,8 @@
return
}
skipServerAuth := jSkipServerAuth == C.JNI_TRUE
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doStartCall(env, context, name, method, skipServerAuth, goPtr, args)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doStartCall(context, name, method, skipServerAuth, goPtr, args)
})
}
@@ -309,43 +308,46 @@
}
//export Java_io_v_impl_google_rpc_StreamImpl_nativeSend
-func Java_io_v_impl_google_rpc_StreamImpl_nativeSend(jenv *C.JNIEnv, jStream C.jobject, goPtr C.jlong, jVomItem C.jbyteArray) {
+func Java_io_v_impl_google_rpc_StreamImpl_nativeSend(jenv *C.JNIEnv, jStream C.jobject, goPtr C.jlong, jVomItem C.jbyteArray, jCallbackObj C.jobject) {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
vomItem := jutil.GoByteArray(env, jutil.Object(uintptr(unsafe.Pointer(jVomItem))))
- item, err := jutil.VomDecodeToValue(vomItem)
- if err != nil {
- jutil.JThrowV(env, err)
- return
- }
- if err := (*(*rpc.Stream)(jutil.NativePtr(goPtr))).Send(item); err != nil {
- jutil.JThrowV(env, err)
- return
- }
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ item, err := jutil.VomDecodeToValue(vomItem)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ return jutil.NullObject, (*(*rpc.Stream)(jutil.NativePtr(goPtr))).Send(item)
+ })
}
//export Java_io_v_impl_google_rpc_StreamImpl_nativeRecv
-func Java_io_v_impl_google_rpc_StreamImpl_nativeRecv(jenv *C.JNIEnv, jStream C.jobject, goPtr C.jlong) C.jbyteArray {
+func Java_io_v_impl_google_rpc_StreamImpl_nativeRecv(jenv *C.JNIEnv, jStream C.jobject, goPtr C.jlong, jCallbackObj C.jobject) {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
result := new(vdl.Value)
- if err := (*(*rpc.Stream)(jutil.NativePtr(goPtr))).Recv(&result); err != nil {
- if err == io.EOF {
- jutil.JThrow(env, jEOFExceptionClass, err.Error())
- return nil
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ if err := (*(*rpc.Stream)(jutil.NativePtr(goPtr))).Recv(&result); err != nil {
+ if err == io.EOF {
+ // Java uses EndOfFile error to detect EOF.
+ err = verror.NewErrEndOfFile(nil)
+ }
+ return jutil.NullObject, err
}
- jutil.JThrowV(env, err)
- return nil
- }
- vomResult, err := vom.Encode(result)
- if err != nil {
- jutil.JThrowV(env, err)
- return nil
- }
- jArr, err := jutil.JByteArray(env, vomResult)
- if err != nil {
- jutil.JThrowV(env, err)
- return nil
- }
- return C.jbyteArray(unsafe.Pointer(jArr))
+ vomResult, err := vom.Encode(result)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
+ jResult, err := jutil.JByteArray(env, vomResult)
+ if err != nil {
+ return jutil.NullObject, err
+ }
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jResult), nil // Un-refed in DoAsyncCall
+ })
}
//export Java_io_v_impl_google_rpc_StreamImpl_nativeFinalize
@@ -354,15 +356,15 @@
}
//export Java_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend
-func Java_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend(jenv *C.JNIEnv, jCall C.jobject, goPtr C.jlong) {
+func Java_io_v_impl_google_rpc_ClientCallImpl_nativeCloseSend(jenv *C.JNIEnv, jCall C.jobject, goPtr C.jlong, jCallbackObj C.jobject) {
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
- if err := (*(*rpc.ClientCall)(jutil.NativePtr(goPtr))).CloseSend(); err != nil {
- jutil.JThrowV(env, err)
- return
- }
+ jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return jutil.NullObject, (*(*rpc.ClientCall)(jutil.NativePtr(goPtr))).CloseSend()
+ })
}
-func doFinish(env jutil.Env, goPtr C.jlong, numResults int) (jutil.Object, error) {
+func doFinish(goPtr C.jlong, numResults int) (jutil.Object, error) {
// Have all the results be decoded into *vdl.Value.
resultPtrs := make([]interface{}, numResults)
for i := 0; i < numResults; i++ {
@@ -384,11 +386,15 @@
return jutil.NullObject, err
}
}
+ env, freeFunc := jutil.GetEnv()
+ defer freeFunc()
jArr, err := jutil.JByteArrayArray(env, vomResults)
if err != nil {
return jutil.NullObject, err
}
- return jArr, nil
+ // Must grab a global reference as we free up the env and all local references that come along
+ // with it.
+ return jutil.NewGlobalRef(env, jArr), nil // Un-refed in DoAsyncCall
}
//export Java_io_v_impl_google_rpc_ClientCallImpl_nativeFinish
@@ -396,8 +402,8 @@
env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
numResults := int(jNumResults)
jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
- jutil.DoAsyncCall(env, jCallback, func(env jutil.Env) (jutil.Object, error) {
- return doFinish(env, goPtr, numResults)
+ jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+ return doFinish(goPtr, numResults)
})
}
diff --git a/test/fortune/fortune.vdl b/test/fortune/fortune.vdl
index 8b9470e..c829464 100644
--- a/test/fortune/fortune.vdl
+++ b/test/fortune/fortune.vdl
@@ -29,12 +29,17 @@
// Get returns a random fortune.
Get() (Fortune string | error) {access.Read}
- // StreamingGet returns a stream that can be used to obtain fortunes.
+ // StreamingGet returns a stream that can be used to obtain fortunes, and returns the
+ // total number of items sent.
StreamingGet() stream<bool, string> (total int32 | error) {access.Read}
// MultipleGet returns the same fortune twice.
MultipleGet() (Fortune string, Another string | error) {access.Read}
+ // MultipleStreamingGet returns a stream that can be used to obtain fortunes, and returns
+ // the total number of items sent, twice.
+ MultipleStreamingGet() stream<bool, string> (total int32, another int32 | 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 ae9937d..7197db2 100644
--- a/test/fortune/fortune.vdl.go
+++ b/test/fortune/fortune.vdl.go
@@ -65,10 +65,14 @@
Add(_ *context.T, Fortune string, _ ...rpc.CallOpt) error
// Get returns a random fortune.
Get(*context.T, ...rpc.CallOpt) (Fortune string, _ error)
- // StreamingGet returns a stream that can be used to obtain fortunes.
+ // StreamingGet returns a stream that can be used to obtain fortunes, and returns the
+ // total number of items sent.
StreamingGet(*context.T, ...rpc.CallOpt) (FortuneStreamingGetClientCall, error)
// MultipleGet returns the same fortune twice.
MultipleGet(*context.T, ...rpc.CallOpt) (Fortune string, Another string, _ error)
+ // MultipleStreamingGet returns a stream that can be used to obtain fortunes, and returns
+ // the total number of items sent, twice.
+ MultipleStreamingGet(*context.T, ...rpc.CallOpt) (FortuneMultipleStreamingGetClientCall, error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, ...rpc.CallOpt) error
// NoTags is a method without tags.
@@ -117,6 +121,15 @@
return
}
+func (c implFortuneClientStub) MultipleStreamingGet(ctx *context.T, opts ...rpc.CallOpt) (ocall FortuneMultipleStreamingGetClientCall, err error) {
+ var call rpc.ClientCall
+ if call, err = v23.GetClient(ctx).StartCall(ctx, c.name, "MultipleStreamingGet", nil, opts...); err != nil {
+ return
+ }
+ ocall = &implFortuneMultipleStreamingGetClientCall{ClientCall: call}
+ 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
@@ -234,6 +247,108 @@
return
}
+// FortuneMultipleStreamingGetClientStream is the client stream for Fortune.MultipleStreamingGet.
+type FortuneMultipleStreamingGetClientStream interface {
+ // RecvStream returns the receiver side of the Fortune.MultipleStreamingGet client stream.
+ RecvStream() interface {
+ // Advance stages an item so that it may be retrieved via Value. Returns
+ // true iff there is an item to retrieve. Advance must be called before
+ // Value is called. May block if an item is not available.
+ Advance() bool
+ // Value returns the item that was staged by Advance. May panic if Advance
+ // returned false or was not called. Never blocks.
+ Value() string
+ // Err returns any error encountered by Advance. Never blocks.
+ Err() error
+ }
+ // SendStream returns the send side of the Fortune.MultipleStreamingGet client stream.
+ SendStream() interface {
+ // Send places the item onto the output stream. Returns errors
+ // encountered while sending, or if Send is called after Close or
+ // the stream has been canceled. Blocks if there is no buffer
+ // space; will unblock when buffer space is available or after
+ // the stream has been canceled.
+ Send(item bool) error
+ // Close indicates to the server that no more items will be sent;
+ // server Recv calls will receive io.EOF after all sent items.
+ // This is an optional call - e.g. a client might call Close if it
+ // needs to continue receiving items from the server after it's
+ // done sending. Returns errors encountered while closing, or if
+ // Close is called after the stream has been canceled. Like Send,
+ // blocks if there is no buffer space available.
+ Close() error
+ }
+}
+
+// FortuneMultipleStreamingGetClientCall represents the call returned from Fortune.MultipleStreamingGet.
+type FortuneMultipleStreamingGetClientCall interface {
+ FortuneMultipleStreamingGetClientStream
+ // Finish performs the equivalent of SendStream().Close, then blocks until
+ // the server is done, and returns the positional return values for the call.
+ //
+ // Finish returns immediately if the call has been canceled; depending on the
+ // timing the output could either be an error signaling cancelation, or the
+ // valid positional return values from the server.
+ //
+ // Calling Finish is mandatory for releasing stream resources, unless the call
+ // has been canceled or any of the other methods return an error. Finish should
+ // be called at most once.
+ Finish() (total int32, another int32, _ error)
+}
+
+type implFortuneMultipleStreamingGetClientCall struct {
+ rpc.ClientCall
+ valRecv string
+ errRecv error
+}
+
+func (c *implFortuneMultipleStreamingGetClientCall) RecvStream() interface {
+ Advance() bool
+ Value() string
+ Err() error
+} {
+ return implFortuneMultipleStreamingGetClientCallRecv{c}
+}
+
+type implFortuneMultipleStreamingGetClientCallRecv struct {
+ c *implFortuneMultipleStreamingGetClientCall
+}
+
+func (c implFortuneMultipleStreamingGetClientCallRecv) Advance() bool {
+ c.c.errRecv = c.c.Recv(&c.c.valRecv)
+ return c.c.errRecv == nil
+}
+func (c implFortuneMultipleStreamingGetClientCallRecv) Value() string {
+ return c.c.valRecv
+}
+func (c implFortuneMultipleStreamingGetClientCallRecv) Err() error {
+ if c.c.errRecv == io.EOF {
+ return nil
+ }
+ return c.c.errRecv
+}
+func (c *implFortuneMultipleStreamingGetClientCall) SendStream() interface {
+ Send(item bool) error
+ Close() error
+} {
+ return implFortuneMultipleStreamingGetClientCallSend{c}
+}
+
+type implFortuneMultipleStreamingGetClientCallSend struct {
+ c *implFortuneMultipleStreamingGetClientCall
+}
+
+func (c implFortuneMultipleStreamingGetClientCallSend) Send(item bool) error {
+ return c.c.Send(item)
+}
+func (c implFortuneMultipleStreamingGetClientCallSend) Close() error {
+ return c.c.CloseSend()
+}
+func (c *implFortuneMultipleStreamingGetClientCall) Finish() (o0 int32, o1 int32, err error) {
+ err = c.ClientCall.Finish(&o0, &o1)
+ return
+}
+
// FortuneServerMethods is the interface a server writer
// implements for Fortune.
//
@@ -243,10 +358,14 @@
Add(_ *context.T, _ rpc.ServerCall, Fortune string) error
// Get returns a random fortune.
Get(*context.T, rpc.ServerCall) (Fortune string, _ error)
- // StreamingGet returns a stream that can be used to obtain fortunes.
+ // StreamingGet returns a stream that can be used to obtain fortunes, and returns the
+ // total number of items sent.
StreamingGet(*context.T, FortuneStreamingGetServerCall) (total int32, _ error)
// MultipleGet returns the same fortune twice.
MultipleGet(*context.T, rpc.ServerCall) (Fortune string, Another string, _ error)
+ // MultipleStreamingGet returns a stream that can be used to obtain fortunes, and returns
+ // the total number of items sent, twice.
+ MultipleStreamingGet(*context.T, FortuneMultipleStreamingGetServerCall) (total int32, another int32, _ error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, rpc.ServerCall) error
// NoTags is a method without tags.
@@ -265,10 +384,14 @@
Add(_ *context.T, _ rpc.ServerCall, Fortune string) error
// Get returns a random fortune.
Get(*context.T, rpc.ServerCall) (Fortune string, _ error)
- // StreamingGet returns a stream that can be used to obtain fortunes.
+ // StreamingGet returns a stream that can be used to obtain fortunes, and returns the
+ // total number of items sent.
StreamingGet(*context.T, *FortuneStreamingGetServerCallStub) (total int32, _ error)
// MultipleGet returns the same fortune twice.
MultipleGet(*context.T, rpc.ServerCall) (Fortune string, Another string, _ error)
+ // MultipleStreamingGet returns a stream that can be used to obtain fortunes, and returns
+ // the total number of items sent, twice.
+ MultipleStreamingGet(*context.T, *FortuneMultipleStreamingGetServerCallStub) (total int32, another int32, _ error)
// GetComplexError returns (always!) ErrComplex.
GetComplexError(*context.T, rpc.ServerCall) error
// NoTags is a method without tags.
@@ -323,6 +446,10 @@
return s.impl.MultipleGet(ctx, call)
}
+func (s implFortuneServerStub) MultipleStreamingGet(ctx *context.T, call *FortuneMultipleStreamingGetServerCallStub) (int32, int32, error) {
+ return s.impl.MultipleStreamingGet(ctx, call)
+}
+
func (s implFortuneServerStub) GetComplexError(ctx *context.T, call rpc.ServerCall) error {
return s.impl.GetComplexError(ctx, call)
}
@@ -370,7 +497,7 @@
},
{
Name: "StreamingGet",
- Doc: "// StreamingGet returns a stream that can be used to obtain fortunes.",
+ Doc: "// StreamingGet returns a stream that can be used to obtain fortunes, and returns the\n// total number of items sent.",
OutArgs: []rpc.ArgDesc{
{"total", ``}, // int32
},
@@ -386,6 +513,15 @@
Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
},
{
+ Name: "MultipleStreamingGet",
+ Doc: "// MultipleStreamingGet returns a stream that can be used to obtain fortunes, and returns\n// the total number of items sent, twice.",
+ OutArgs: []rpc.ArgDesc{
+ {"total", ``}, // int32
+ {"another", ``}, // int32
+ },
+ Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
+ },
+ {
Name: "GetComplexError",
Doc: "// GetComplexError returns (always!) ErrComplex.",
Tags: []*vdl.Value{vdl.ValueOf(access.Tag("Read"))},
@@ -485,3 +621,87 @@
func (s implFortuneStreamingGetServerCallSend) Send(item string) error {
return s.s.Send(item)
}
+
+// FortuneMultipleStreamingGetServerStream is the server stream for Fortune.MultipleStreamingGet.
+type FortuneMultipleStreamingGetServerStream interface {
+ // RecvStream returns the receiver side of the Fortune.MultipleStreamingGet server stream.
+ RecvStream() interface {
+ // Advance stages an item so that it may be retrieved via Value. Returns
+ // true iff there is an item to retrieve. Advance must be called before
+ // Value is called. May block if an item is not available.
+ Advance() bool
+ // Value returns the item that was staged by Advance. May panic if Advance
+ // returned false or was not called. Never blocks.
+ Value() bool
+ // Err returns any error encountered by Advance. Never blocks.
+ Err() error
+ }
+ // SendStream returns the send side of the Fortune.MultipleStreamingGet server stream.
+ SendStream() interface {
+ // Send places the item onto the output stream. Returns errors encountered
+ // while sending. Blocks if there is no buffer space; will unblock when
+ // buffer space is available.
+ Send(item string) error
+ }
+}
+
+// FortuneMultipleStreamingGetServerCall represents the context passed to Fortune.MultipleStreamingGet.
+type FortuneMultipleStreamingGetServerCall interface {
+ rpc.ServerCall
+ FortuneMultipleStreamingGetServerStream
+}
+
+// FortuneMultipleStreamingGetServerCallStub is a wrapper that converts rpc.StreamServerCall into
+// a typesafe stub that implements FortuneMultipleStreamingGetServerCall.
+type FortuneMultipleStreamingGetServerCallStub struct {
+ rpc.StreamServerCall
+ valRecv bool
+ errRecv error
+}
+
+// Init initializes FortuneMultipleStreamingGetServerCallStub from rpc.StreamServerCall.
+func (s *FortuneMultipleStreamingGetServerCallStub) Init(call rpc.StreamServerCall) {
+ s.StreamServerCall = call
+}
+
+// RecvStream returns the receiver side of the Fortune.MultipleStreamingGet server stream.
+func (s *FortuneMultipleStreamingGetServerCallStub) RecvStream() interface {
+ Advance() bool
+ Value() bool
+ Err() error
+} {
+ return implFortuneMultipleStreamingGetServerCallRecv{s}
+}
+
+type implFortuneMultipleStreamingGetServerCallRecv struct {
+ s *FortuneMultipleStreamingGetServerCallStub
+}
+
+func (s implFortuneMultipleStreamingGetServerCallRecv) Advance() bool {
+ s.s.errRecv = s.s.Recv(&s.s.valRecv)
+ return s.s.errRecv == nil
+}
+func (s implFortuneMultipleStreamingGetServerCallRecv) Value() bool {
+ return s.s.valRecv
+}
+func (s implFortuneMultipleStreamingGetServerCallRecv) Err() error {
+ if s.s.errRecv == io.EOF {
+ return nil
+ }
+ return s.s.errRecv
+}
+
+// SendStream returns the send side of the Fortune.MultipleStreamingGet server stream.
+func (s *FortuneMultipleStreamingGetServerCallStub) SendStream() interface {
+ Send(item string) error
+} {
+ return implFortuneMultipleStreamingGetServerCallSend{s}
+}
+
+type implFortuneMultipleStreamingGetServerCallSend struct {
+ s *FortuneMultipleStreamingGetServerCallStub
+}
+
+func (s implFortuneMultipleStreamingGetServerCallSend) Send(item string) error {
+ return s.s.Send(item)
+}
diff --git a/util/call.go b/util/call.go
index 31ff97f..57560f4 100644
--- a/util/call.go
+++ b/util/call.go
@@ -295,7 +295,8 @@
// DoAsyncCall invokes the given fnToWrap in a goroutine. If fnToWrap returns an
// error, the given callback's onFailure method is invoked with the error as a
// parameter. If fnToWrap succeeds, its Object result is passed as a
-// parameter to the callback's onSuccess method.
+// parameter to the callback's onSuccess method. fnToWrap must return a global reference
+// to any non-null objects - this reference will be deleted by DoAsyncCall.
//
// The caller of doAsyncCall must take care that no local JNI references are
// used in fnToWrap's closure. For example:
@@ -312,31 +313,39 @@
// JNI references are only valid in the scope of a particular thread. You are
// free to capture any pure-Go variables and we recommend that you use that
// approach to pass parameters through to fnToWrap.
-func DoAsyncCall(env Env, jCallback Object, fnToWrap func(env Env) (Object, error)) {
+func DoAsyncCall(env Env, jCallback Object, fnToWrap func() (Object, error)) {
go func(jCallback Object) {
+ jResult, err := fnToWrap() // probably blocking, so don't call GetEnv() before this line
env, freeFunc := GetEnv()
defer freeFunc()
defer DeleteGlobalRef(env, jCallback)
- if result, err := fnToWrap(env); err != nil {
- CallbackOnFailure(env, jCallback, err)
- } else {
- CallbackOnSuccess(env, jCallback, result)
+ if !jResult.IsNull() {
+ if !IsGlobalRef(env, jResult) {
+ CallbackOnFailure(env, jCallback, fmt.Errorf("Function passed to DoAsyncCall must return global object references"))
+ return
+ }
+ defer DeleteGlobalRef(env, jResult)
}
+ if err != nil {
+ CallbackOnFailure(env, jCallback, err)
+ return
+ }
+ CallbackOnSuccess(env, jCallback, jResult)
}(NewGlobalRef(env, jCallback))
}
// CallbackOnFailure calls the given callback's "onFailure" method with the given error and
// panic-s if the method couldn't be invoked.
-func CallbackOnFailure(env Env, callback Object, err error) {
- if err := CallVoidMethod(env, callback, "onFailure", []Sign{VExceptionSign}, err); err != nil {
+func CallbackOnFailure(env Env, jCallback Object, err error) {
+ if err := CallVoidMethod(env, jCallback, "onFailure", []Sign{VExceptionSign}, err); err != nil {
panic(fmt.Sprintf("couldn't call Java onFailure method: %v", err))
}
}
// CalbackOnSuccess calls the given callback's "onSuccess" method with the given result
// and panic-s if the method couldn't be invoked.
-func CallbackOnSuccess(env Env, callback Object, jClientCall Object) {
- if err := CallVoidMethod(env, callback, "onSuccess", []Sign{ObjectSign}, jClientCall); err != nil {
+func CallbackOnSuccess(env Env, jCallback Object, jResult Object) {
+ if err := CallVoidMethod(env, jCallback, "onSuccess", []Sign{ObjectSign}, jResult); err != nil {
panic(fmt.Sprintf("couldn't call Java onSuccess method: %v", err))
}
}
diff --git a/util/jni_wrapper.c b/util/jni_wrapper.c
index 21c4a74..054a94c 100644
--- a/util/jni_wrapper.c
+++ b/util/jni_wrapper.c
@@ -166,6 +166,10 @@
(*env)->DeleteGlobalRef(env, globalRef);
}
+jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
+ return (*env)->GetObjectRefType(env, obj);
+}
+
jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
return (*env)->GetJavaVM(env, vm);
}
diff --git a/util/jni_wrapper.h b/util/jni_wrapper.h
index 2241c30..ef66a03 100644
--- a/util/jni_wrapper.h
+++ b/util/jni_wrapper.h
@@ -115,6 +115,10 @@
// Deletes the global reference pointed to by globalRef.
void DeleteGlobalRef(JNIEnv* env, jobject globalRef);
+// Returns the type of the object referred to by the obj argument.
+// The argument obj can either be a local, global or weak global reference.
+jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);
+
// Returns the Java VM interface (used in the Invocation API) associated with
// the current thread.
jint GetJavaVM(JNIEnv* env, JavaVM** vm);
diff --git a/util/ref.go b/util/ref.go
index 2b19433..7fdbc11 100644
--- a/util/ref.go
+++ b/util/ref.go
@@ -40,6 +40,16 @@
C.DeleteLocalRef(env.value(), obj.value())
}
+// IsGlobalRef returns true iff the reference pointed to by obj is a global reference.
+func IsGlobalRef(env Env, obj Object) bool {
+ return C.GetObjectRefType(env.value(), obj.value()) == C.JNIGlobalRefType
+}
+
+// IsLocalRef returns true iff the reference pointed to by obj is a local reference.
+func IsLocalRef(env Env, obj Object) bool {
+ return C.GetObjectRefType(env.value(), obj.value()) == C.JNILocalRefType
+}
+
// 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().
diff --git a/util/util.go b/util/util.go
index 18cdde5..f2d97bd 100644
--- a/util/util.go
+++ b/util/util.go
@@ -233,7 +233,6 @@
C.AttachCurrentThreadAsDaemon(jVM, &jenv, nil)
}
env = Env(uintptr(unsafe.Pointer(jenv)))
- //env := Env{jenv}
// GetEnv is called by Go code that wishes to call Java methods. In
// this case, JNI cannot automatically free unused local refererences.
// We must do it manually by pushing a new local reference frame. The