From 2c8e86144b4327bb6497f6164fe029ad5af74398 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Wed, 1 Nov 2023 09:35:06 -0400 Subject: [PATCH] Fixes for returning objects (#1797) Currently, `lower()` always returns a borrow of the object handle. This is fine for function arguments, since you know the object is still alive on the stack while the function is being called. However, for function returns this is not correct. To fix this: clone the handle in `lower()`. Added a test for this -- it was surprisingly easy to cause a segfault with the current behavior. --- fixtures/coverall/src/coverall.udl | 4 ++++ fixtures/coverall/src/lib.rs | 5 +++- fixtures/coverall/src/traits.rs | 16 ++++++++++++- .../coverall/tests/bindings/test_coverall.kts | 11 +++++++++ .../coverall/tests/bindings/test_coverall.py | 11 +++++++++ .../tests/bindings/test_coverall.swift | 13 +++++++++++ .../kotlin/templates/ObjectRuntime.kt | 8 ++++++- .../kotlin/templates/ObjectTemplate.kt | 2 +- .../python/templates/ObjectTemplate.py | 11 +++++---- .../src/bindings/python/templates/macros.py | 6 ++--- .../bindings/ruby/templates/ObjectTemplate.rb | 13 ++++++++--- .../swift/templates/ObjectTemplate.swift | 12 ++++++---- uniffi_bindgen/src/interface/ffi.rs | 23 +++++++++++++++++++ uniffi_bindgen/src/interface/object.rs | 20 +++++++++------- uniffi_macros/src/export/scaffolding.rs | 5 ++-- uniffi_macros/src/export/trait_interface.rs | 18 +++++++++++++-- uniffi_macros/src/object.rs | 23 ++++++++++++++++--- uniffi_meta/src/ffi_names.rs | 6 +++++ uniffi_meta/src/lib.rs | 8 +++++++ 19 files changed, 180 insertions(+), 35 deletions(-) diff --git a/fixtures/coverall/src/coverall.udl b/fixtures/coverall/src/coverall.udl index b59d4bce7d..6f56125ec2 100644 --- a/fixtures/coverall/src/coverall.udl +++ b/fixtures/coverall/src/coverall.udl @@ -22,6 +22,9 @@ namespace coverall { void test_getters(Getters g); sequence ancestor_names(NodeTrait node); + + Getters test_round_trip_through_rust(Getters getters); + void test_round_trip_through_foreign(Getters getters); }; dictionary SimpleDict { @@ -209,6 +212,7 @@ interface Getters { string? get_option(string v, boolean arg2); sequence get_list(sequence v, boolean arg2); void get_nothing(string v); + Coveralls round_trip_object(Coveralls coveralls); }; // Test trait #2 diff --git a/fixtures/coverall/src/lib.rs b/fixtures/coverall/src/lib.rs index 9916dadb67..9206079635 100644 --- a/fixtures/coverall/src/lib.rs +++ b/fixtures/coverall/src/lib.rs @@ -10,7 +10,10 @@ use std::time::SystemTime; use once_cell::sync::Lazy; mod traits; -pub use traits::{ancestor_names, get_traits, make_rust_getters, test_getters, Getters, NodeTrait}; +pub use traits::{ + ancestor_names, get_traits, make_rust_getters, test_getters, test_round_trip_through_foreign, + test_round_trip_through_rust, Getters, NodeTrait, +}; static NUM_ALIVE: Lazy> = Lazy::new(|| RwLock::new(0)); diff --git a/fixtures/coverall/src/traits.rs b/fixtures/coverall/src/traits.rs index 15785ef0c6..5ba65694ec 100644 --- a/fixtures/coverall/src/traits.rs +++ b/fixtures/coverall/src/traits.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use super::{ComplexError, CoverallError}; +use super::{ComplexError, CoverallError, Coveralls}; use std::sync::{Arc, Mutex}; // namespace functions. @@ -41,6 +41,16 @@ pub trait Getters: Send + Sync { fn get_option(&self, v: String, arg2: bool) -> Result, ComplexError>; fn get_list(&self, v: Vec, arg2: bool) -> Vec; fn get_nothing(&self, v: String); + fn round_trip_object(&self, coveralls: Arc) -> Arc; +} + +pub fn test_round_trip_through_rust(getters: Arc) -> Arc { + getters +} + +pub fn test_round_trip_through_foreign(getters: Arc) { + let coveralls = getters.round_trip_object(Arc::new(Coveralls::new("round-trip".to_owned()))); + assert_eq!(coveralls.get_name(), "round-trip"); } struct RustGetters; @@ -90,6 +100,10 @@ impl Getters for RustGetters { } fn get_nothing(&self, _v: String) {} + + fn round_trip_object(&self, coveralls: Arc) -> Arc { + coveralls + } } pub fn make_rust_getters() -> Arc { diff --git a/fixtures/coverall/tests/bindings/test_coverall.kts b/fixtures/coverall/tests/bindings/test_coverall.kts index 2370bd346e..dc2623b947 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.kts +++ b/fixtures/coverall/tests/bindings/test_coverall.kts @@ -262,6 +262,10 @@ class KotlinGetters : Getters { @Suppress("UNUSED_PARAMETER") override fun getNothing(v: String) = Unit + + override fun roundTripObject(coveralls: Coveralls): Coveralls { + return coveralls + } } // Test traits implemented in Rust @@ -377,6 +381,13 @@ getTraits().let { traits -> // not possible through the `NodeTrait` interface (see #1787). } +makeRustGetters().let { rustGetters -> + // Check that these don't cause use-after-free bugs + testRoundTripThroughRust(rustGetters) + + testRoundTripThroughForeign(KotlinGetters()) +} + // This tests that the UniFFI-generated scaffolding doesn't introduce any unexpected locking. // We have one thread busy-wait for a some period of time, while a second thread repeatedly // increments the counter and then checks if the object is still busy. The second thread should diff --git a/fixtures/coverall/tests/bindings/test_coverall.py b/fixtures/coverall/tests/bindings/test_coverall.py index 17593bc833..33375cbfeb 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.py +++ b/fixtures/coverall/tests/bindings/test_coverall.py @@ -314,6 +314,9 @@ def get_list(self, v, arg2): def get_nothing(self, _v): return None + def round_trip_object(self, coveralls): + return coveralls + class PyNode: def __init__(self): self.parent = None @@ -413,5 +416,13 @@ def test_path(self): py_node.set_parent(None) traits[0].set_parent(None) + def test_round_tripping(self): + rust_getters = make_rust_getters(); + coveralls = Coveralls("test_round_tripping") + # Check that these don't cause use-after-free bugs + test_round_trip_through_rust(rust_getters) + + test_round_trip_through_foreign(PyGetters()) + if __name__=='__main__': unittest.main() diff --git a/fixtures/coverall/tests/bindings/test_coverall.swift b/fixtures/coverall/tests/bindings/test_coverall.swift index c6fcba4290..6508ed331d 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.swift +++ b/fixtures/coverall/tests/bindings/test_coverall.swift @@ -283,6 +283,10 @@ class SwiftGetters: Getters { func getList(v: [Int32], arg2: Bool) -> [Int32] { arg2 ? v : [] } func getNothing(v: String) -> () { } + + func roundTripObject(coveralls: Coveralls) -> Coveralls { + return coveralls + } } @@ -412,3 +416,12 @@ do { swiftNode.setParent(parent: nil) traits[0].setParent(parent: nil) } + +// Test round tripping +do { + let rustGetters = makeRustGetters() + // Check that these don't cause use-after-free bugs + let _ = testRoundTripThroughRust(getters: rustGetters) + + testRoundTripThroughForeign(getters: SwiftGetters()) +} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt index 604155a4d1..157e257c01 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt @@ -149,6 +149,12 @@ abstract class FFIObject: Disposable, AutoCloseable { // To be overridden in subclasses. } + fun uniffiCloneHandle(): UniffiHandle { + return rustCall() { status -> + _UniFFILib.INSTANCE.{{ obj.ffi_object_clone().name() }}(handle!!, status) + } + } + override fun destroy() { // Only allow a single call to this method. // TODO: maybe we should log a warning if called more than once? @@ -179,7 +185,7 @@ abstract class FFIObject: Disposable, AutoCloseable { } while (! this.callCounter.compareAndSet(c, c + 1L)) // Now we can safely do the method call without the handle being freed concurrently. try { - return block(this.handle!!) + return block(this.uniffiCloneHandle()) } finally { // This decrement always matches the increment we performed above. if (this.callCounter.decrementAndGet() == 0L) { diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index 53518abb61..5f436d01fd 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -156,7 +156,7 @@ public object {{ obj|ffi_converter_name }}: FfiConverter<{{ type_name }}, Uniffi override fun lower(value: {{ type_name }}): UniffiHandle { {%- match obj.imp() %} {%- when ObjectImpl::Struct %} - return value.handle!! + return value.uniffiCloneHandle() {%- when ObjectImpl::Trait %} return handleMap.newHandle(value) {%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index c3e30d5be7..48b20688bb 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -21,6 +21,9 @@ def __del__(self): if handle is not None: _rust_call(_UniffiLib.{{ obj.ffi_object_free().name() }}, handle) + def uniffi_clone_handle(self): + return _rust_call(_UniffiLib.{{ obj.ffi_object_clone().name() }}, self._uniffi_handle) + # Used by alternative constructors or any methods which return this type. @classmethod def _make_instance_(cls, handle): @@ -54,13 +57,13 @@ def __eq__(self, other: object) -> {{ eq.return_type().unwrap()|type_name }}: if not isinstance(other, {{ type_name }}): return NotImplemented - return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_handle", eq) %}) + return {{ eq.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self.uniffi_clone_handle()", eq) %}) def __ne__(self, other: object) -> {{ ne.return_type().unwrap()|type_name }}: if not isinstance(other, {{ type_name }}): return NotImplemented - return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self._uniffi_handle", ne) %}) + return {{ ne.return_type().unwrap()|lift_fn }}({% call py::to_ffi_call_with_prefix("self.uniffi_clone_handle()", ne) %}) {%- when UniffiTrait::Hash { hash } %} {%- call py::method_decl("__hash__", hash) %} {% endmatch %} @@ -95,9 +98,7 @@ def check(value: {{ type_name }}): def lower(value: {{ type_name }}): {%- match obj.imp() %} {%- when ObjectImpl::Struct %} - if not isinstance(value, {{ impl_name }}): - raise TypeError("Expected {{ impl_name }} instance, {} found".format(type(value).__name__)) - return value._uniffi_handle + return value.uniffi_clone_handle() {%- when ObjectImpl::Trait %} return {{ ffi_converter_name }}._handle_map.new_handle(value) {%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 1fddb2df83..94562aaf5b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -106,7 +106,7 @@ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}): {%- call setup_args_extra_indent(meth) %} return _uniffi_rust_call_async( _UniffiLib.{{ meth.ffi_func().name() }}( - self._uniffi_handle, {% call arg_list_lowered(meth) %} + self.uniffi_clone_handle(), {% call arg_list_lowered(meth) %} ), _UniffiLib.{{ meth.ffi_rust_future_poll(ci) }}, _UniffiLib.{{ meth.ffi_rust_future_complete(ci) }}, @@ -135,14 +135,14 @@ def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}): def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}) -> "{{ return_type|type_name }}": {%- call setup_args_extra_indent(meth) %} return {{ return_type|lift_fn }}( - {% call to_ffi_call_with_prefix("self._uniffi_handle", meth) %} + {% call to_ffi_call_with_prefix("self.uniffi_clone_handle()", meth) %} ) {%- when None %} def {{ py_method_name }}(self, {% call arg_list_decl(meth) %}): {%- call setup_args_extra_indent(meth) %} - {% call to_ffi_call_with_prefix("self._uniffi_handle", meth) %} + {% call to_ffi_call_with_prefix("self.uniffi_clone_handle()", meth) %} {% endmatch %} {% endif %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb index b63c1e21ca..efeea1362d 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb @@ -32,7 +32,14 @@ def self._uniffi_check(inst) end def self._uniffi_lower(inst) - return inst.instance_variable_get :@handle + return inst._uniffi_clone_handle() + end + + def _uniffi_clone_handle() + return {{ ci.namespace()|class_name_rb }}.rust_call( + :{{ obj.ffi_object_clone().name() }}, + @handle + ) end {%- match obj.primary_constructor() %} @@ -62,14 +69,14 @@ def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- when Some with (return_type) -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::setup_args_extra_indent(meth) %} - result = {% call rb::to_ffi_call_with_prefix("@handle", meth) %} + result = {% call rb::to_ffi_call_with_prefix("_uniffi_clone_handle()", meth) %} return {{ "result"|lift_rb(return_type) }} end {%- when None -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::setup_args_extra_indent(meth) %} - {% call rb::to_ffi_call_with_prefix("@handle", meth) %} + {% call rb::to_ffi_call_with_prefix("_uniffi_clone_handle()", meth) %} end {% endmatch %} {% endfor %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index e272d0efb7..c26ccb2e23 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -28,6 +28,10 @@ public class {{ impl_class_name }}: self.handle = handle } + public func uniffiCloneHandle() -> UInt64 { + return try! rustCall { {{ obj.ffi_object_clone().name() }}(self.handle, $0) } + } + {%- match obj.primary_constructor() %} {%- when Some with (cons) %} public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} { @@ -56,7 +60,7 @@ public class {{ impl_class_name }}: return {% call swift::try(meth) %} await uniffiRustCallAsync( rustFutureFunc: { {{ meth.ffi_func().name() }}( - self.handle + self.uniffiCloneHandle() {%- for arg in meth.arguments() -%} , {{ arg|lower_fn }}({{ arg.name()|var_name }}) @@ -89,14 +93,14 @@ public class {{ impl_class_name }}: public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} -> {{ return_type|type_name }} { return {% call swift::try(meth) %} {{ return_type|lift_fn }}( - {% call swift::to_ffi_call_with_prefix("self.handle", meth) %} + {% call swift::to_ffi_call_with_prefix("self.uniffiCloneHandle()", meth) %} ) } {%- when None %} public func {{ meth.name()|fn_name }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} { - {% call swift::to_ffi_call_with_prefix("self.handle", meth) %} + {% call swift::to_ffi_call_with_prefix("self.uniffiCloneHandle()", meth) %} } {%- endmatch -%} @@ -158,7 +162,7 @@ public struct {{ ffi_converter_name }}: FfiConverter { public static func lower(_ value: {{ type_name }}) -> UInt64 { {%- match obj.imp() %} {%- when ObjectImpl::Struct %} - return value.handle + return value.uniffiCloneHandle() {%- when ObjectImpl::Trait %} return handleMap.newHandle(obj: value) {%- endmatch %} diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index de8db86334..9c9e183dd4 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -141,6 +141,29 @@ pub struct FfiFunction { } impl FfiFunction { + pub fn ffi_clone(name: String) -> Self { + Self { + name, + arguments: vec![FfiArgument { + name: "handle".to_owned(), + type_: FfiType::Handle, + }], + return_type: Some(FfiType::Handle), + ..Default::default() + } + } + + pub fn ffi_free(name: String) -> Self { + Self { + name, + arguments: vec![FfiArgument { + name: "handle".to_owned(), + type_: FfiType::Handle, + }], + ..Default::default() + } + } + pub fn callback_init(module_path: &str, trait_name: &str) -> Self { Self { name: uniffi_meta::init_callback_fn_symbol_name(module_path, trait_name), diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 290e412598..00ee9b2695 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -57,8 +57,6 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` -use std::iter; - use anyhow::Result; use uniffi_meta::Checksum; @@ -99,6 +97,8 @@ pub struct Object { // hash value we're trying to calculate here, so excluding it // avoids a weird circular dependency in the calculation. #[checksum_ignore] + pub(super) ffi_func_clone: FfiFunction, + #[checksum_ignore] pub(super) ffi_func_free: FfiFunction, // Ffi function to initialize the foreign callback for trait interfaces #[checksum_ignore] @@ -158,6 +158,10 @@ impl Object { self.uniffi_traits.iter().collect() } + pub fn ffi_object_clone(&self) -> &FfiFunction { + &self.ffi_func_clone + } + pub fn ffi_object_free(&self) -> &FfiFunction { &self.ffi_func_free } @@ -169,7 +173,8 @@ impl Object { } pub fn iter_ffi_function_definitions(&self) -> impl Iterator { - iter::once(&self.ffi_func_free) + [&self.ffi_func_clone, &self.ffi_func_free] + .into_iter() .chain(&self.ffi_init_callback) .chain(self.constructors.iter().map(|f| &f.ffi_func)) .chain(self.methods.iter().map(|f| &f.ffi_func)) @@ -236,7 +241,8 @@ impl AsType for Object { impl From for Object { fn from(meta: uniffi_meta::ObjectMetadata) -> Self { - let ffi_free_name = meta.free_ffi_symbol_name(); + let ffi_func_clone = FfiFunction::ffi_clone(meta.clone_ffi_symbol_name()); + let ffi_func_free = FfiFunction::ffi_free(meta.free_ffi_symbol_name()); Object { module_path: meta.module_path, name: meta.name, @@ -244,10 +250,8 @@ impl From for Object { constructors: Default::default(), methods: Default::default(), uniffi_traits: Default::default(), - ffi_func_free: FfiFunction { - name: ffi_free_name, - ..Default::default() - }, + ffi_func_clone, + ffi_func_free, ffi_init_callback: None, } } diff --git a/uniffi_macros/src/export/scaffolding.rs b/uniffi_macros/src/export/scaffolding.rs index 7a33015f06..172951f1f9 100644 --- a/uniffi_macros/src/export/scaffolding.rs +++ b/uniffi_macros/src/export/scaffolding.rs @@ -143,14 +143,13 @@ impl ScaffoldingBits { // pointer. quote! { { - let foreign_arc = ::std::boxed::Box::leak(unsafe { Box::from_raw(uniffi_self_lowered.as_raw() as *mut ::std::sync::Arc) }); - // Take a clone for our own use. - Ok(::std::sync::Arc::clone(foreign_arc)) + Ok(>::consume_handle(uniffi_self_lowered)) } } } else { quote! { #lift_impl::try_lift(uniffi_self_lowered) } }; + let lift_closure = sig.lift_closure(Some(quote! { match #try_lift_self { Ok(v) => v, diff --git a/uniffi_macros/src/export/trait_interface.rs b/uniffi_macros/src/export/trait_interface.rs index cab3297281..3f84409d1d 100644 --- a/uniffi_macros/src/export/trait_interface.rs +++ b/uniffi_macros/src/export/trait_interface.rs @@ -13,7 +13,6 @@ use crate::{ object::interface_meta_static_var, util::{derive_ffi_traits, ident_to_string, tagged_impl_header}, }; -use uniffi_meta::free_fn_symbol_name; pub(super) fn gen_trait_scaffolding( mod_path: &str, @@ -28,12 +27,27 @@ pub(super) fn gen_trait_scaffolding( let trait_name = ident_to_string(&self_ident); let trait_impl = callback_interface::trait_impl(mod_path, &self_ident, &items) .unwrap_or_else(|e| e.into_compile_error()); + let clone_fn_ident = Ident::new( + &uniffi_meta::clone_fn_symbol_name(mod_path, &trait_name), + Span::call_site(), + ); let free_fn_ident = Ident::new( - &free_fn_symbol_name(mod_path, &trait_name), + &uniffi_meta::free_fn_symbol_name(mod_path, &trait_name), Span::call_site(), ); let free_tokens = quote! { + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn #clone_fn_ident( + handle: ::uniffi::Handle, + call_status: &mut ::uniffi::RustCallStatus + ) -> ::uniffi::Handle { + uniffi::rust_call(call_status, || { + Ok(>::clone_handle(handle)) + }) + } + #[doc(hidden)] #[no_mangle] pub extern "C" fn #free_fn_ident( diff --git a/uniffi_macros/src/object.rs b/uniffi_macros/src/object.rs index 81e5a4f0ad..59222d0283 100644 --- a/uniffi_macros/src/object.rs +++ b/uniffi_macros/src/object.rs @@ -1,7 +1,6 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; use syn::DeriveInput; -use uniffi_meta::free_fn_symbol_name; use crate::util::{create_metadata_items, ident_to_string, mod_path, tagged_impl_header}; @@ -9,7 +8,14 @@ pub fn expand_object(input: DeriveInput, udl_mode: bool) -> syn::Result syn::Result ::uniffi::Handle { + uniffi::rust_call(call_status, || { + Ok(<#ident as ::uniffi::HandleAlloc>::clone_handle(handle)) + }) + } + #[doc(hidden)] #[no_mangle] pub extern "C" fn #free_fn_ident( @@ -64,7 +81,7 @@ pub(crate) fn interface_impl(ident: &Ident, udl_mode: bool) -> TokenStream { } fn try_lift(handle: Self::FfiType) -> ::uniffi::Result<::std::sync::Arc> { - Ok(<#ident as ::uniffi::HandleAlloc>::get_arc(handle)) + Ok(<#ident as ::uniffi::HandleAlloc>::consume_handle(handle)) } fn write(obj: ::std::sync::Arc, buf: &mut Vec) { diff --git a/uniffi_meta/src/ffi_names.rs b/uniffi_meta/src/ffi_names.rs index 44a5bc3e63..ebc125410b 100644 --- a/uniffi_meta/src/ffi_names.rs +++ b/uniffi_meta/src/ffi_names.rs @@ -39,6 +39,12 @@ pub fn free_fn_symbol_name(namespace: &str, object_name: &str) -> String { format!("uniffi_{namespace}_fn_free_{object_name}") } +/// FFI symbol name for the `clone` function for an object. +pub fn clone_fn_symbol_name(namespace: &str, object_name: &str) -> String { + let object_name = object_name.to_ascii_lowercase(); + format!("uniffi_{namespace}_fn_clone_{object_name}") +} + /// FFI symbol name for the `init_callback` function for a callback interface pub fn init_callback_fn_symbol_name(namespace: &str, callback_interface_name: &str) -> String { let callback_interface_name = callback_interface_name.to_ascii_lowercase(); diff --git a/uniffi_meta/src/lib.rs b/uniffi_meta/src/lib.rs index 1c8a66801c..c99c142b3f 100644 --- a/uniffi_meta/src/lib.rs +++ b/uniffi_meta/src/lib.rs @@ -319,6 +319,14 @@ pub struct CallbackInterfaceMetadata { } impl ObjectMetadata { + /// FFI symbol name for the `clone` function for this object. + /// + /// This function is used to increment the reference count before lowering an object to pass + /// back to Rust. + pub fn clone_ffi_symbol_name(&self) -> String { + clone_fn_symbol_name(&self.module_path, &self.name) + } + /// FFI symbol name for the `free` function for this object. /// /// This function is used to free the memory used by this object.