// GoAuthorizer converts the given Java authorizer into a Go authorizer. func GoAuthorizer(env jutil.Env, jAuth jutil.Object) (security.Authorizer, error) { if jAuth.IsNull() { return nil, nil } if jutil.IsInstanceOf(env, jAuth, jPermissionsAuthorizerClass) { // Called with our implementation of Authorizer, which maintains a Go reference - use it. ref, err := jutil.JLongField(env, jAuth, "nativeRef") if err != nil { return nil, err } return *(*security.Authorizer)(jutil.GoRefValue(jutil.Ref(ref))), nil } // Reference Java dispatcher; it will be de-referenced when the go // dispatcher created below is garbage-collected (through the finalizer // callback we setup below). jAuth = jutil.NewGlobalRef(env, jAuth) a := &authorizer{ jAuth: jAuth, } runtime.SetFinalizer(a, func(a *authorizer) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, a.jAuth) }) return a, nil }
//export Java_io_v_impl_google_lib_discovery_UpdateImpl_nativeAttachment func Java_io_v_impl_google_lib_discovery_UpdateImpl_nativeAttachment(jenv *C.JNIEnv, _ C.jobject, goRef C.jlong, jCtx C.jobject, jName C.jstring, jCbObj C.jobject) { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) ctx, _, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jCtx)))) if err != nil { jutil.JThrowV(env, err) return } update := *(*discovery.Update)(jutil.GoRefValue(jutil.Ref(goRef))) name := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jName)))) jCb := jutil.Object(uintptr(unsafe.Pointer(jCbObj))) jutil.DoAsyncCall(env, jCb, func() (jutil.Object, error) { dataOrErr := <-update.Attachment(ctx, name) if dataOrErr.Error != nil { return jutil.NullObject, err } env, freeFunc := jutil.GetEnv() defer freeFunc() jData, err := jutil.JByteArray(env, dataOrErr.Data) 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, jData), nil // Un-refed in DoAsyncCall }) }
// GoBlessingStore creates an instance of security.BlessingStore that uses the // provided Java BlessingStore as its underlying implementation. func GoBlessingStore(env jutil.Env, jBlessingStore jutil.Object) (security.BlessingStore, error) { if jBlessingStore.IsNull() { return nil, nil } if jutil.IsInstanceOf(env, jBlessingStore, jBlessingStoreImplClass) { // Called with our implementation of BlessingStore, which maintains a Go reference - use it. ref, err := jutil.JLongField(env, jBlessingStore, "nativeRef") if err != nil { return nil, err } return (*(*security.BlessingStore)(jutil.GoRefValue(jutil.Ref(ref)))), nil } // Reference Java BlessingStore; it will be de-referenced when the Go // BlessingStore created below is garbage-collected (through the finalizer // callback we setup just below). jBlessingStore = jutil.NewGlobalRef(env, jBlessingStore) s := &blessingStore{ jBlessingStore: jBlessingStore, } runtime.SetFinalizer(s, func(s *blessingStore) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, s.jBlessingStore) }) return s, nil }
func initDriverFactory(env jutil.Env) error { jDriverClass, err := jutil.JFindClass(env, "io/v/android/impl/google/discovery/plugins/ble/Driver") if err != nil { return err } factory := func(ctx *context.T, _ string) (ble.Driver, error) { env, freeFunc := jutil.GetEnv() defer freeFunc() jCtx, err := jcontext.JavaContext(env, ctx, nil) if err != nil { return nil, err } jDriver, err := jutil.NewObject(env, jDriverClass, []jutil.Sign{contextSign}, jCtx) if err != nil { return nil, err } // Reference the driver; it will be de-referenced when the driver is garbage-collected. jDriver = jutil.NewGlobalRef(env, jDriver) d := &driver{jDriver} runtime.SetFinalizer(d, func(*driver) { env, freeFunc := jutil.GetEnv() jutil.DeleteGlobalRef(env, d.jDriver) freeFunc() }) return d, nil } ble.SetDriverFactory(factory) return nil }
// GoPrincipal converts the provided Java VPrincipal object into a Go Principal. func GoPrincipal(env jutil.Env, jPrincipal jutil.Object) (security.Principal, error) { if jPrincipal.IsNull() { return nil, nil } if jutil.IsInstanceOf(env, jPrincipal, jVPrincipalImplClass) { // Called with our implementation of VPrincipal, which maintains a Go reference - use it. ref, err := jutil.CallLongMethod(env, jPrincipal, "nativeRef", nil) if err != nil { return nil, err } return (*(*security.Principal)(jutil.GoRefValue(jutil.Ref(ref)))), nil } // Reference Java VPrincipal; it will be de-referenced when the Go Principal // created below is garbage-collected (through the finalizer callback we // setup just below). jPrincipal = jutil.NewGlobalRef(env, jPrincipal) // Create Go Principal. p := &principal{ jPrincipal: jPrincipal, } runtime.SetFinalizer(p, func(p *principal) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, p.jPrincipal) }) return p, nil }
// GoCall creates instance of security.Call that uses the provided Java // Call as its underlying implementation. func GoCall(env jutil.Env, jCall jutil.Object) (security.Call, error) { if jCall.IsNull() { return nil, nil } if jutil.IsInstanceOf(env, jCall, jCallImplClass) { // Called with our implementation of Call, which maintains a Go reference - use it. ref, err := jutil.CallLongMethod(env, jCall, "nativeRef", nil) if err != nil { return nil, err } return (*(*security.Call)(jutil.GoRefValue(jutil.Ref(ref)))), nil } // Reference Java call; it will be de-referenced when the go call // created below is garbage-collected (through the finalizer callback we // setup just below). jCall = jutil.NewGlobalRef(env, jCall) call := &callImpl{ jCall: jCall, } runtime.SetFinalizer(call, func(c *callImpl) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, c.jCall) }) return call, nil }
//export Java_io_v_impl_google_rpc_StreamImpl_nativeRecv func Java_io_v_impl_google_rpc_StreamImpl_nativeRecv(jenv *C.JNIEnv, jStream C.jobject, goRef C.jlong, jCallbackObj C.jobject) { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj))) result := new(vdl.Value) jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) { if err := (*(*rpc.Stream)(jutil.GoRefValue(jutil.Ref(goRef)))).Recv(&result); err != nil { if err == io.EOF { // Java uses EndOfFile error to detect EOF. err = verror.NewErrEndOfFile(nil) } return jutil.NullObject, err } 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 }) }
func doFinish(goRef 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++ { value := new(vdl.Value) resultPtrs[i] = &value } if err := (*(*rpc.ClientCall)(jutil.GoRefValue(jutil.Ref(goRef)))).Finish(resultPtrs...); err != nil { // Invocation error. return jutil.NullObject, err } // VOM-encode the results. vomResults := make([][]byte, numResults) for i, resultPtr := range resultPtrs { // Remove the pointer from the result. Simply *resultPtr doesn't work // as resultPtr is of type interface{}. result := interface{}(jutil.DerefOrDie(resultPtr)) var err error if vomResults[i], err = vom.Encode(result); err != nil { return jutil.NullObject, err } } env, freeFunc := jutil.GetEnv() defer freeFunc() jArr, err := jutil.JByteArrayArray(env, vomResults) 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, jArr), nil // Un-refed in DoAsyncCall }
// GoBlessingRoots creates an instance of security.BlessingRoots that uses the // provided Java BlessingRoots as its underlying implementation. func GoBlessingRoots(env jutil.Env, jBlessingRoots jutil.Object) (security.BlessingRoots, error) { if jBlessingRoots.IsNull() { return nil, nil } if jutil.IsInstanceOf(env, jBlessingRoots, jBlessingRootsImplClass) { // Called with our implementation of BlessingRoots, which maintains a Go reference - use it. ref, err := jutil.CallLongMethod(env, jBlessingRoots, "nativeRef", nil) if err != nil { return nil, err } return (*(*security.BlessingRoots)(jutil.GoRefValue(jutil.Ref(ref)))), nil } // Reference Java BlessingRoots; it will be de-referenced when the Go // BlessingRoots created below is garbage-collected (through the finalizer // callback we setup just below). jBlessingRoots = jutil.NewGlobalRef(env, jBlessingRoots) r := &blessingRoots{ jBlessingRoots: jBlessingRoots, } runtime.SetFinalizer(r, func(r *blessingRoots) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, r.jBlessingRoots) }) return r, nil }
// javaOutputWriter translates an implementation of the Java Vango.OutputWriter interface to Go's io.Writer func newJavaOutputWriter(env jutil.Env, o jutil.Object) io.Writer { ret := &javaOutputWriter{jutil.NewGlobalRef(env, o)} runtime.SetFinalizer(ret, func(w *javaOutputWriter) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, w.obj) }) return ret }
func newListener(env jutil.Env, jListener jutil.Object) flow.Listener { // Reference Java Listener; it will be de-referenced when the Go Listener // created below is garbage-collected (through the finalizer callback we // setup just below). jListener = jutil.NewGlobalRef(env, jListener) l := &btListener{jListener} runtime.SetFinalizer(l, func(l *btListener) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, l.jListener) }) return l }
//export Java_io_v_v23_syncbase_DatabaseImpl_nativeListenForInvites func Java_io_v_v23_syncbase_DatabaseImpl_nativeListenForInvites(jenv *C.JNIEnv, jDatabase C.jobject, jContext C.jobject, jInviteHandler C.jobject) { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) database := jutil.Object(uintptr(unsafe.Pointer(jDatabase))) handler := jutil.Object(uintptr(unsafe.Pointer(jInviteHandler))) ctx, _, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext)))) if err != nil { jutil.JThrowV(env, err) return } jDbId, err := jutil.CallObjectMethod(env, database, "id", nil, idSign) if err != nil { jutil.JThrowV(env, err) return } dbName, err := jutil.CallStringMethod(env, jDbId, "getName", nil) if err != nil { jutil.JThrowV(env, err) return } dbBlessing, err := jutil.CallStringMethod(env, jDbId, "getBlessing", nil) if err != nil { jutil.JThrowV(env, err) return } dbId := wire.Id{Name: dbName, Blessing: dbBlessing} // Note: There is no need to use a buffered channel here. ListenForInvites // spawns a goroutine for this listener that acts as an infinite buffer so we can // process invites at our own pace. ch := make(chan discovery.Invite) if err := discovery.ListenForInvites(ctx, dbId, ch); err != nil { jutil.JThrowV(env, err) return } handler = jutil.NewGlobalRef(env, handler) go func() { for invite := range ch { if err := handleInvite(invite, handler); err != nil { // TODO(mattr): We should cancel the stream and return an error to // the user here. ctx.Errorf("couldn't call invite handler: %v", err) } } env, free := jutil.GetEnv() jutil.DeleteGlobalRef(env, handler) free() }() }
// GoAddressChooser converts a Java AddressChooser object into a Go address // chooser function. func GoAddressChooser(env jutil.Env, jChooser jutil.Object) rpc.AddressChooser { // Reference Java chooser; it will be de-referenced when the go function // created below is garbage-collected (through the finalizer callback we // setup just below). chooser := &jniAddressChooser{ jChooser: jutil.NewGlobalRef(env, jChooser), } runtime.SetFinalizer(chooser, func(chooser *jniAddressChooser) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, chooser.jChooser) }) return chooser }
func (l *btListener) Accept(ctx *context.T) (flow.Conn, error) { env, freeFunc := jutil.GetEnv() defer freeFunc() jStream, err := jutil.CallObjectMethod(env, l.jListener, "accept", nil, streamSign) if err != nil { return nil, err } // Reference Java Stream; it will be de-referenced when the Go connection // created below is garbage-collected (through the finalizer callback we // setup just below). jStream = jutil.NewGlobalRef(env, jStream) return newConnection(env, jStream), nil }
// GoContextValue returns the Go Context value given the Java Context value. func GoContextValue(env jutil.Env, jValue jutil.Object) (interface{}, error) { // Reference Java object; it will be de-referenced when the Go wrapper // object created below is garbage-collected (via the finalizer we setup // just below.) jValue = jutil.NewGlobalRef(env, jValue) value := &goContextValue{ jObj: jValue, } runtime.SetFinalizer(value, func(value *goContextValue) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, value.jObj) }) return value, nil }
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 } 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 }
func goInvoker(env jutil.Env, jInvoker jutil.Object) (rpc.Invoker, error) { // 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 = jutil.NewGlobalRef(env, jInvoker) i := &invoker{ jInvoker: jInvoker, } runtime.SetFinalizer(i, func(i *invoker) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, i.jInvoker) }) return i, nil }
// GoSigner creates an instance of security.Signer that uses the provided // Java VSigner as its underlying implementation. func GoSigner(env jutil.Env, jSigner jutil.Object) (security.Signer, error) { // Reference Java VSigner; it will be de-referenced when the Go Signer // created below is garbage-collected (through the finalizer callback we // setup just below). jSigner = jutil.NewGlobalRef(env, jSigner) s := &signer{ jSigner: jSigner, } runtime.SetFinalizer(s, func(s *signer) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, s.jSigner) }) return s, nil }
// GoDispatcher creates a new rpc.Dispatcher given the Java Dispatcher object. func GoDispatcher(env jutil.Env, jDispatcher jutil.Object) (rpc.Dispatcher, error) { // Reference Java dispatcher; it will be de-referenced when the go // dispatcher created below is garbage-collected (through the finalizer // callback we setup below). jDispatcher = jutil.NewGlobalRef(env, jDispatcher) d := &dispatcher{ jDispatcher: jDispatcher, } runtime.SetFinalizer(d, func(d *dispatcher) { env, freeFunc := jutil.GetEnv() defer freeFunc() jutil.DeleteGlobalRef(env, d.jDispatcher) }) return d, nil }
func (btProtocol) Dial(ctx *context.T, protocol, address string, timeout time.Duration) (flow.Conn, error) { env, freeFunc := jutil.GetEnv() defer freeFunc() jContext, err := jcontext.JavaContext(env, ctx, nil) if err != nil { return nil, err } jStream, err := jutil.CallStaticObjectMethod(env, jBluetoothClass, "dial", []jutil.Sign{contextSign, jutil.StringSign, jutil.DurationSign}, streamSign, jContext, address, timeout) if err != nil { return nil, err } // Reference Java Stream; it will be de-referenced when the Go connection // created below is garbage-collected (through the finalizer callback we // setup just below). jStream = jutil.NewGlobalRef(env, jStream) return newConnection(env, jStream), nil }
func doStartCall(context *context.T, cancel func(), name, method string, opts []rpc.CallOpt, goRef C.jlong, args []interface{}) (jutil.Object, error) { // Invoke StartCall call, err := (*(*rpc.Client)(jutil.GoRefValue(jutil.Ref(goRef)))).StartCall(context, name, method, args, opts...) if err != nil { return jutil.NullObject, err } env, freeFunc := jutil.GetEnv() defer freeFunc() jContext, err := jcontext.JavaContext(env, context, cancel) if err != nil { return jutil.NullObject, err } jCall, err := javaCall(env, jContext, call) 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, jCall), nil // Un-refed in DoAsyncCall }
//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGlob func Java_io_v_impl_google_namespace_NamespaceImpl_nativeGlob(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong, jContext C.jobject, jPattern C.jstring, jOptions C.jobject) C.jobject { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) n := *(*namespace.T)(jutil.GoRefValue(jutil.Ref(goRef))) ctx, cancel, pattern, opts, err := globArgs(env, jContext, jPattern, jOptions) if err != nil { jutil.JThrowV(env, err) return nil } var globChannel <-chan naming.GlobReply var globError error globDone := make(chan bool) go func() { globChannel, globError = n.Glob(ctx, pattern, opts...) close(globDone) }() jChannel, err := jchannel.JavaInputChannel(env, ctx, cancel, func() (jutil.Object, error) { // A few blocking calls below - don't call GetEnv() before they complete. <-globDone if globError != nil { return jutil.NullObject, globError } globReply, ok := <-globChannel if !ok { return jutil.NullObject, verror.NewErrEndOfFile(ctx) } env, freeFunc := jutil.GetEnv() defer freeFunc() jReply, err := jutil.JVomCopy(env, globReply, jGlobReplyClass) 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, jReply), nil // Un-refed by InputChannelImpl_nativeRecv }) if err != nil { jutil.JThrowV(env, err) return nil } return C.jobject(unsafe.Pointer(jChannel)) }
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 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_lib_discovery_DiscoveryImpl_nativeScan func Java_io_v_impl_google_lib_discovery_DiscoveryImpl_nativeScan(jenv *C.JNIEnv, _ C.jobject, goRef C.jlong, jCtx C.jobject, jQuery C.jstring) C.jobject { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) ctx, _, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jCtx)))) if err != nil { jutil.JThrowV(env, err) return nil } d := *(*discovery.T)(jutil.GoRefValue(jutil.Ref(goRef))) query := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jQuery)))) scanCh, err := d.Scan(ctx, query) if err != nil { jutil.JThrowV(env, err) return nil } jChannel, err := jchannel.JavaInputChannel(env, ctx, nil, func() (jutil.Object, error) { update, ok := <-scanCh if !ok { return jutil.NullObject, verror.NewErrEndOfFile(ctx) } env, freeFunc := jutil.GetEnv() defer freeFunc() jUpdate, err := javaUpdate(env, update) 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, jUpdate), nil // un-refed by InputChannelImpl_nativeRecv }) if err != nil { jutil.JThrowV(env, err) return nil } return C.jobject(unsafe.Pointer(jChannel)) }
//export Java_io_v_v23_context_VContext_nativeOnDone func Java_io_v_v23_context_VContext_nativeOnDone(jenv *C.JNIEnv, jVContext C.jobject, goRef C.jlong, jCallbackObj C.jobject) { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj))) ctx := (*(*context.T)(jutil.GoRefValue(jutil.Ref(goRef)))) c := ctx.Done() if c == nil { jutil.CallbackOnFailure(env, jCallback, errors.New("Context isn't cancelable")) return } jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) { <-c env, freeFunc := jutil.GetEnv() defer freeFunc() jReason, err := JavaContextDoneReason(env, ctx.Err()) 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, jReason), nil // Un-refed in DoAsyncCall }) }
//export Java_io_v_impl_google_rpc_ClientImpl_nativeStartCall func Java_io_v_impl_google_rpc_ClientImpl_nativeStartCall(jenv *C.JNIEnv, jClientObj C.jobject, goRef C.jlong, jContext C.jobject, jName C.jstring, jMethod C.jstring, jVomArgs C.jobjectArray, jOptionsObj C.jobject, jCallbackObj C.jobject) { env := jutil.Env(uintptr(unsafe.Pointer(jenv))) name := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jName)))) method := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jMethod)))) jOptions := jutil.Object(uintptr(unsafe.Pointer(jOptionsObj))) jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj))) ctx, cancel, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext)))) if err != nil { jutil.CallbackOnFailure(env, jCallback, err) return } args, err := decodeArgs(env, jVomArgs) if err != nil { jutil.CallbackOnFailure(env, jCallback, err) return } opts, err := jopts.GoRpcOpts(env, jOptions) if err != nil { jutil.CallbackOnFailure(env, jCallback, err) return } // Create a global reference to the client object for the duration of the call // so that the java object doesn't get garbage collected while the async call // is happening. This is an issue because if the java object is garbage collected, // the go ref will also be collected causing doStartCall to panic. jClient := jutil.NewGlobalRef(env, jutil.Object(uintptr(unsafe.Pointer(jClientObj)))) jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) { obj, err := doStartCall(ctx, cancel, name, method, opts, goRef, args) env, freeFunc := jutil.GetEnv() jutil.DeleteGlobalRef(env, jClient) freeFunc() return obj, err }) }