Fix early finalization of function.

Previously, the SetFinalizer(&f, ...) call would allow f to be finalized
as soon as the pointer &f was no longer accessible (i.e. immediately
after the SetFinalizer call finished). This caused the AddressChooser to
be finalized while Java still referenced it and before it was called in
some instances.

Now a pointer to struct is returned and the finalizer is only called
after the last pointer to the struct is dropped.

MultiPart: 4/4
Change-Id: Ia1751e22a8673e4a1e72b62cd3321ce1ed84169d
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index 9e5f737..1c3c30f 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -9,7 +9,6 @@
 import (
 	"io"
 	"log"
-	"net"
 	"unsafe"
 
 	"v.io/v23/options"
@@ -551,7 +550,7 @@
 		jutil.JThrowV(env, err)
 		return nil
 	}
-	addrs, err := (*((*func(protocol string, candidates []net.Addr) ([]net.Addr, error))(jutil.Ptr(goPtr))))(protocol, candidates)
+	addrs, err := (*(*rpc.AddressChooser)(jutil.Ptr(goPtr))).ChooseAddress(protocol, candidates)
 	if err != nil {
 		jutil.JThrowV(env, err)
 		return nil
diff --git a/impl/google/rpc/util.go b/impl/google/rpc/util.go
index 0c52ce0..8b7c892 100644
--- a/impl/google/rpc/util.go
+++ b/impl/google/rpc/util.go
@@ -411,7 +411,7 @@
 // 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 JavaAddressChooser(jEnv interface{}, chooser func(protocol string, candidates []net.Addr) ([]net.Addr, error)) (unsafe.Pointer, error) {
+func JavaAddressChooser(jEnv interface{}, chooser rpc.AddressChooser) (unsafe.Pointer, error) {
 	jAddressChooser, err := jutil.NewObject(jEnv, jAddressChooserImplClass, []jutil.Sign{jutil.LongSign}, int64(jutil.PtrValue(&chooser)))
 	if err != nil {
 		return nil, err
@@ -420,34 +420,41 @@
 	return jAddressChooser, nil
 }
 
+type jniAddressChooser struct {
+	jChooser C.jobject
+}
+
+func (chooser *jniAddressChooser) ChooseAddress(protocol string, candidates []net.Addr) ([]net.Addr, error) {
+	javaEnv, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+	jCandidates, err := JavaNetworkAddressArray(javaEnv, candidates)
+	if err != nil {
+		return nil, err
+	}
+	addrsSign := jutil.ArraySign(jutil.ClassSign("io.v.v23.rpc.NetworkAddress"))
+	jAddrs, err := jutil.CallObjectMethod(javaEnv, chooser.jChooser, "choose", []jutil.Sign{jutil.StringSign, addrsSign}, addrsSign, protocol, jCandidates)
+	if err != nil {
+		return nil, err
+	}
+	return GoNetworkAddressArray(javaEnv, jAddrs)
+}
+
 // GoAddressChooser converts a Java AddressChooser object into a Go address
 // chooser function.
 // 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 GoAddressChooser(jEnv, jChooserObj interface{}) func(protocol string, candidates []net.Addr) ([]net.Addr, error) {
+func GoAddressChooser(jEnv, jChooserObj interface{}) 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).
-	jChooser := jutil.NewGlobalRef(jEnv, jChooserObj)
-	f := func(protocol string, candidates []net.Addr) ([]net.Addr, error) {
-		javaEnv, freeFunc := jutil.GetEnv()
-		defer freeFunc()
-		jCandidates, err := JavaNetworkAddressArray(javaEnv, candidates)
-		if err != nil {
-			return nil, err
-		}
-		addrsSign := jutil.ArraySign(jutil.ClassSign("io.v.v23.rpc.NetworkAddress"))
-		jAddrs, err := jutil.CallObjectMethod(javaEnv, jChooser, "choose", []jutil.Sign{jutil.StringSign, addrsSign}, addrsSign, protocol, jCandidates)
-		if err != nil {
-			return nil, err
-		}
-		return GoNetworkAddressArray(javaEnv, jAddrs)
+	chooser := &jniAddressChooser{
+		jChooser: C.jobject(jutil.NewGlobalRef(jEnv, jChooserObj)),
 	}
-	runtime.SetFinalizer(&f, func(f *func(protocol string, candidates []net.Addr) ([]net.Addr, error)) {
+	runtime.SetFinalizer(chooser, func(chooser *jniAddressChooser) {
 		javaEnv, freeFunc := jutil.GetEnv()
 		defer freeFunc()
-		jutil.DeleteGlobalRef(javaEnv, jChooser)
+		jutil.DeleteGlobalRef(javaEnv, chooser.jChooser)
 	})
-	return f
+	return chooser
 }