diff --git a/Cargo.lock b/Cargo.lock index 33fca38..7fdb190 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1219,6 +1219,7 @@ dependencies = [ "uniffi-fixture-type-limits", "uniffi-go-fixture-destroy", "uniffi-go-fixture-errors", + "uniffi-go-fixture-issue43", "uniffi-go-fixture-issue45", "uniffi-go-fixture-name-case", "uniffi-go-fixture-objects", @@ -1447,6 +1448,16 @@ dependencies = [ "uniffi_macros", ] +[[package]] +name = "uniffi-go-fixture-issue43" +version = "1.0.0" +dependencies = [ + "uniffi", + "uniffi-go-fixture-issue45", + "uniffi_build", + "uniffi_macros", +] + [[package]] name = "uniffi-go-fixture-issue45" version = "1.0.0" diff --git a/bindgen/templates/CallbackInterfaceRuntime.go b/bindgen/templates/CallbackInterfaceRuntime.go index b3312fe..52f1a9f 100644 --- a/bindgen/templates/CallbackInterfaceRuntime.go +++ b/bindgen/templates/CallbackInterfaceRuntime.go @@ -1,48 +1,38 @@ {% if self.include_once_check("CallbackHelpers.go") %}{% include "CallbackHelpers.go" %}{% endif %} type concurrentHandleMap[T any] struct { - leftMap map[uint64]*T - rightMap map[*T]uint64 + handles map[uint64]T currentHandle uint64 lock sync.RWMutex } func newConcurrentHandleMap[T any]() *concurrentHandleMap[T] { return &concurrentHandleMap[T]{ - leftMap: map[uint64]*T{}, - rightMap: map[*T]uint64{}, + handles: map[uint64]T{}, } } -func (cm *concurrentHandleMap[T]) insert(obj *T) uint64 { +func (cm *concurrentHandleMap[T]) insert(obj T) uint64 { cm.lock.Lock() defer cm.lock.Unlock() - if existingHandle, ok := cm.rightMap[obj]; ok { - return existingHandle - } cm.currentHandle = cm.currentHandle + 1 - cm.leftMap[cm.currentHandle] = obj - cm.rightMap[obj] = cm.currentHandle + cm.handles[cm.currentHandle] = obj return cm.currentHandle } -func (cm *concurrentHandleMap[T]) remove(handle uint64) bool { +func (cm *concurrentHandleMap[T]) remove(handle uint64) { cm.lock.Lock() defer cm.lock.Unlock() - if val, ok := cm.leftMap[handle]; ok { - delete(cm.leftMap, handle) - delete(cm.rightMap, val) - } - return false + delete(cm.handles, handle) } -func (cm *concurrentHandleMap[T]) tryGet(handle uint64) (*T, bool) { +func (cm *concurrentHandleMap[T]) tryGet(handle uint64) (T, bool) { cm.lock.RLock() defer cm.lock.RUnlock() - val, ok := cm.leftMap[handle] + val, ok := cm.handles[handle] return val, ok } @@ -60,7 +50,7 @@ func (c *FfiConverterCallbackInterface[CallbackInterface]) Lift(handle uint64) C if !ok { panic(fmt.Errorf("no callback in handle map: %d", handle)) } - return *val + return val } func (c *FfiConverterCallbackInterface[CallbackInterface]) Read(reader io.Reader) CallbackInterface { @@ -68,7 +58,7 @@ func (c *FfiConverterCallbackInterface[CallbackInterface]) Read(reader io.Reader } func (c *FfiConverterCallbackInterface[CallbackInterface]) Lower(value CallbackInterface) C.uint64_t { - return C.uint64_t(c.handleMap.insert(&value)) + return C.uint64_t(c.handleMap.insert(value)) } func (c *FfiConverterCallbackInterface[CallbackInterface]) Write(writer io.Writer, value CallbackInterface) { diff --git a/binding_tests/fixture_callbacks_test.go b/binding_tests/fixture_callbacks_test.go index c2ebcdc..9b12f3a 100644 --- a/binding_tests/fixture_callbacks_test.go +++ b/binding_tests/fixture_callbacks_test.go @@ -1,6 +1,8 @@ package binding_tests import ( + "fmt" + "strings" "testing" "github.com/NordSecurity/uniffi-bindgen-go/binding_tests/generated/fixture_callbacks" @@ -73,6 +75,29 @@ func (invalidGetters) GetNothing(v string) *fixture_callbacks.SimpleError { return fixture_callbacks.NewSimpleErrorBadArgument() } +type goStringifier struct{} + +func (goStringifier) FromSimpleType(value int32) string { + return fmt.Sprintf("Go: %d", value) +} + +func (goStringifier) FromComplexType(values *[]*float64) string { + if values == nil { + return "Go: nil" + } + + var strNumbers []string + for _, num := range *values { + if num != nil { + strNumbers = append(strNumbers, fmt.Sprintf("%f", *num)) + } else { + strNumbers = append(strNumbers, "nil") + } + } + + return fmt.Sprintf("Go: %s", strings.Join(strNumbers, " ")) +} + type testGetterInput[T any] struct { name string value T @@ -312,3 +337,18 @@ func TestRustGetters_GetStringOptionalCallback(t *testing.T) { }) } } + +func TestRustGetters_CallbackReferenceDoesNotInvalidateOtherReferences(t *testing.T) { + stringifier := goStringifier{} + rustStringifier1 := fixture_callbacks.NewRustStringifier(stringifier) + + { + rustStringifier2 := fixture_callbacks.NewRustStringifier(stringifier) + assert.Equal(t, "Go: 123", rustStringifier2.FromSimpleType(123)) + rustStringifier2.Destroy() + // `stringifier` must remain valid after `rustStringifier2` drops the reference + } + + assert.Equal(t, "Go: 321", rustStringifier1.FromSimpleType(321)) + rustStringifier1.Destroy() +} diff --git a/binding_tests/issue45_test.go b/binding_tests/issue45_test.go index cbf07ca..fc6874a 100644 --- a/binding_tests/issue45_test.go +++ b/binding_tests/issue45_test.go @@ -6,8 +6,8 @@ package binding_tests import ( "github.com/NordSecurity/uniffi-bindgen-go/binding_tests/generated/issue45" - "testing" "github.com/stretchr/testify/assert" + "testing" ) // Ensure multiple futures packages work fine together, the other one being