From af1d8a9b8f6df5620f1d6e90f4f0808932eae348 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Wed, 10 Feb 2021 16:47:25 +1100 Subject: [PATCH] Add support for factory functions and static methods. It's a fairly common pattern for object-oriented interfaces to have named functions or static methods that act as named "factories" for producing object instances in ways that differ from the default constructor. This commit adds basic support for such factories by: * Allowing owned object instances to be returned from functions and methods. * Allowing interface methods to be marked as `static`. The "owned object instances" part here is really key, since it allows us to bypass a lot of the general concerns about returning *references* to object instances that are outlined in #197 (and that this commit does not at all attempt to tackle). --- docs/manual/src/udl/interfaces.md | 19 ++++ examples/sprites/src/lib.rs | 6 ++ examples/sprites/src/sprites.udl | 2 +- .../sprites/tests/bindings/test_sprites.kts | 8 +- .../sprites/tests/bindings/test_sprites.py | 4 + .../sprites/tests/bindings/test_sprites.swift | 3 +- .../sprites/tests/test_generated_bindings.rs | 2 +- .../gecko_js/templates/InterfaceTemplate.cpp | 3 + .../gecko_js/templates/WebIDLTemplate.webidl | 2 +- .../kotlin/templates/ObjectTemplate.kt | 35 +++++++- .../src/bindings/python/gen_python.rs | 2 +- .../src/bindings/python/templates/Helpers.py | 19 ++++ .../python/templates/ObjectTemplate.py | 19 ++++ .../src/bindings/python/templates/wrapper.py | 1 + .../swift/templates/ObjectTemplate.swift | 28 +++++- uniffi_bindgen/src/interface/function.rs | 3 - uniffi_bindgen/src/interface/object.rs | 86 +++++++++++++++---- .../src/templates/ObjectTemplate.rs | 36 ++++++++ uniffi_bindgen/src/templates/macros.rs | 19 +++- 19 files changed, 263 insertions(+), 34 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/python/templates/Helpers.py diff --git a/docs/manual/src/udl/interfaces.md b/docs/manual/src/udl/interfaces.md index 465dab869a..cf1e85eed9 100644 --- a/docs/manual/src/udl/interfaces.md +++ b/docs/manual/src/udl/interfaces.md @@ -74,6 +74,25 @@ func display(list: TodoListProtocol) { } ``` +## Static Methods + +Interface methods can be marked with the `static` keyword to mark them as belonging to the interface +itself rather than to a particular instance. Static methods are commonly used to make named alternatives to +the default constructor, like this: + +```idl +interface TodoList { + // The default constructor makes an empty list. + constructor(); + // This static method is a shortcut for making a new TodoList from a list of items. + static TodoList new_from_items(sequence) + ... +``` + +UniFFI will expose an appropriate static-method, class-method or similar in the foreign language binding, +and will connect it to the Rust method of the same name on the underlying Rust struct. + + ## Concurrent Access Since interfaces represent mutable data, uniffi has to take extra care diff --git a/examples/sprites/src/lib.rs b/examples/sprites/src/lib.rs index 3721e37a21..2b1a6b65b3 100644 --- a/examples/sprites/src/lib.rs +++ b/examples/sprites/src/lib.rs @@ -39,6 +39,12 @@ impl Sprite { } } + fn new_relative_to(reference: Point, direction: Vector) -> Sprite { + Sprite { + current_position: translate(&reference, direction), + } + } + fn get_position(&self) -> Point { self.current_position.clone() } diff --git a/examples/sprites/src/sprites.udl b/examples/sprites/src/sprites.udl index e76c33ac08..8f49f19df0 100644 --- a/examples/sprites/src/sprites.udl +++ b/examples/sprites/src/sprites.udl @@ -14,8 +14,8 @@ dictionary Vector { }; interface Sprite { - // Should be an optional, but I had to test nullable args :) constructor(Point? initial_position); + static Sprite new_relative_to(Point reference, Vector direction); Point get_position(); void move_to(Point position); void move_by(Vector direction); diff --git a/examples/sprites/tests/bindings/test_sprites.kts b/examples/sprites/tests/bindings/test_sprites.kts index a023e11ab5..42451f28dd 100644 --- a/examples/sprites/tests/bindings/test_sprites.kts +++ b/examples/sprites/tests/bindings/test_sprites.kts @@ -13,9 +13,13 @@ s.moveBy(Vector(-4.0, 2.0)) assert( s.getPosition() == Point(-3.0, 4.0) ) s.destroy() -try { +try { s.moveBy(Vector(0.0, 0.0)) assert(false) { "Should not be able to call anything after `destroy`" } } catch(e: IllegalStateException) { assert(true) -} \ No newline at end of file +} + +val srel = Sprite.newRelativeTo(Point(0.0, 1.0), Vector(1.0, 1.5)) +assert( srel.getPosition() == Point(1.0, 2.5) ) + diff --git a/examples/sprites/tests/bindings/test_sprites.py b/examples/sprites/tests/bindings/test_sprites.py index 45c130c8c9..5142c2fc42 100644 --- a/examples/sprites/tests/bindings/test_sprites.py +++ b/examples/sprites/tests/bindings/test_sprites.py @@ -11,3 +11,7 @@ s.move_by(Vector(-4, 2)) assert s.get_position() == Point(-3, 4) + +srel = Sprite.new_relative_to(Point(0, 1), Vector(1, 1.5)) +assert srel.get_position() == Point(1, 2.5) + diff --git a/examples/sprites/tests/bindings/test_sprites.swift b/examples/sprites/tests/bindings/test_sprites.swift index 8ff51ac597..d5428ac679 100644 --- a/examples/sprites/tests/bindings/test_sprites.swift +++ b/examples/sprites/tests/bindings/test_sprites.swift @@ -12,4 +12,5 @@ assert( s.getPosition() == Point(x: 1, y: 2)) s.moveBy(direction: Vector(dx: -4, dy: 2)) assert( s.getPosition() == Point(x: -3, y: 4)) - +let srel = Sprite.newRelativeTo(reference: Point(x: 0.0, y: 1.0), direction: Vector(dx: 1, dy: 1.5)) +assert( srel.getPosition() == Point(x: 1.0, y: 2.5) ) diff --git a/examples/sprites/tests/test_generated_bindings.rs b/examples/sprites/tests/test_generated_bindings.rs index d9539977de..fa4354c096 100644 --- a/examples/sprites/tests/test_generated_bindings.rs +++ b/examples/sprites/tests/test_generated_bindings.rs @@ -1,7 +1,7 @@ uniffi_macros::build_foreign_language_testcases!( "src/sprites.udl", [ - // "tests/bindings/test_sprites.py", + "tests/bindings/test_sprites.py", "tests/bindings/test_sprites.kts", "tests/bindings/test_sprites.swift", ] diff --git a/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp b/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp index f1843a15ea..398eb9f590 100644 --- a/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp +++ b/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp @@ -64,6 +64,9 @@ already_AddRefed<{{ obj.name()|class_name_cpp(context) }}> {{ obj.name()|class_n {%- endfor %} {%- for meth in obj.methods() %} +{% if meth.is_static() %} +MOZ_STATIC_ASSERT(false, "Sorry the gecko-js backend does not yet support static methods"); +{% endif %} {% match meth.cpp_return_type() %}{% when Some with (type_) %}{{ type_|ret_type_cpp(context) }}{% else %}void{% endmatch %} {{ obj.name()|class_name_cpp(context) }}::{{ meth.name()|fn_name_cpp }}( {%- for arg in meth.cpp_arguments() %} diff --git a/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl b/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl index eddd93fdec..05830dfcff 100644 --- a/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl +++ b/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl @@ -66,7 +66,7 @@ interface {{ obj.name()|class_name_webidl(context) }} { {%- if meth.throws().is_some() %} [Throws] {% endif %} - {%- match meth.webidl_return_type() -%}{%- when Some with (type_) %}{{ type_|type_webidl(context) }}{% when None %}void{% endmatch %} {{ meth.name()|fn_name_webidl }}( + {%- match meth.webidl_return_type() -%}{%- when Some with (type_) %}{{ type_|type_webidl(context) }}{% when None %}void{% endmatch %} {% if meth.is_static() %}static {% endif -%} {{ meth.name()|fn_name_webidl }}( {%- for arg in meth.arguments() %} {% if arg.optional() -%}optional{%- else -%}{%- endif %} {{ arg.webidl_type()|type_webidl(context) }} {{ arg.name() }} {%- match arg.webidl_default_value() %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index c2ab9a223f..84d7e0af61 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -1,10 +1,12 @@ public interface {{ obj.name()|class_name_kt }}Interface { {% for meth in obj.methods() -%} + {%- if ! meth.is_static() -%} fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}) {%- match meth.return_type() -%} {%- when Some with (return_type) %}: {{ return_type|type_kt -}} {%- else -%} - {%- endmatch %} + {%- endmatch -%} + {%- endif %} {% endfor %} } @@ -38,7 +40,9 @@ class {{ obj.name()|class_name_kt }}( } } + {# // Instance methods #} {% for meth in obj.methods() -%} + {%- if ! meth.is_static() -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -48,12 +52,37 @@ class {{ obj.name()|class_name_kt }}( }.let { {{ "it"|lift_kt(return_type) }} } - + {%- when None -%} override fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_protocol(meth) %}) = callWithHandle { - {%- call kt::to_ffi_call_with_prefix("it", meth) %} + {%- call kt::to_ffi_call_with_prefix("it", meth) %} } {% endmatch %} + {%- endif -%} {% endfor %} + + companion object { + internal fun lift(handle: Long): {{ obj.name()|class_name_kt }} { + return {{ obj.name()|class_name_kt }}(handle) + } + + {# // Static methods, if any #} + {% for meth in obj.methods() -%} + {%- if meth.is_static() -%} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}): {{ return_type|type_kt }} { + val _retval = {% call kt::to_ffi_call(meth) %} + return {{ "_retval"|lift_kt(return_type) }} + } + + {%- when None -%} + fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}) = + {% call kt::to_ffi_call(meth) %} + {% endmatch %} + {%- endif -%} + {% endfor %} + } } diff --git a/uniffi_bindgen/src/bindings/python/gen_python.rs b/uniffi_bindgen/src/bindings/python/gen_python.rs index 6c086c5995..dc922879f9 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python.rs @@ -181,7 +181,7 @@ mod filters { Type::Boolean => format!("(True if {} else False)", nm), Type::String => format!("{}.consumeIntoString()", nm), Type::Enum(name) => format!("{}({})", class_name_py(name)?, nm), - Type::Object(_) => panic!("No support for lifting objects, yet"), + Type::Object(name) => format!("liftObject({}, {})", class_name_py(name)?, nm), Type::CallbackInterface(_) => panic!("No support for lifting callback interfaces, yet"), Type::Error(_) => panic!("No support for lowering errors, yet"), Type::Record(_) | Type::Optional(_) | Type::Sequence(_) | Type::Map(_) => format!( diff --git a/uniffi_bindgen/src/bindings/python/templates/Helpers.py b/uniffi_bindgen/src/bindings/python/templates/Helpers.py new file mode 100644 index 0000000000..2b5bafd777 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/Helpers.py @@ -0,0 +1,19 @@ +# Miscellaneous "private" helpers. +# +# These are things that we need to have available for internal use, +# but that we don't want to expose as part of the public API classes, +# even as "hidden" methods. + +{% if ci.iter_object_definitions().len() > 0 %} +def liftObject(cls, handle): + """Helper to create an object instace from a handle. + + This bypasses the usual __init__() logic for the given class + and just directly creates an instance with the given handle. + It's used to support factory functions and methods, which need + to return instances without invoking a (Python-level) constructor. + """ + obj = cls.__new__(cls) + obj._handle = handle + return obj +{% endif %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 930e670635..a583d0cb3d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -14,6 +14,24 @@ def __del__(self): ) {% for meth in obj.methods() -%} + {%- if meth.is_static() -%} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + @staticmethod + def {{ meth.name()|fn_name_py }}({% call py::arg_list_decl(meth) %}): + {%- call py::coerce_args_extra_indent(meth) %} + _retval = {% call py::to_ffi_call(meth) %} + return {{ "_retval"|lift_py(return_type) }} + + {%- when None -%} + @staticmethod + def {{ meth.name()|fn_name_py }}({% call py::arg_list_decl(meth) %}): + {%- call py::coerce_args_extra_indent(meth) %} + {% call py::to_ffi_call(meth) %} + {% endmatch %} + + {%- else -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -27,4 +45,5 @@ def {{ meth.name()|fn_name_py }}(self, {% call py::arg_list_decl(meth) %}): {%- call py::coerce_args_extra_indent(meth) %} {% call py::to_ffi_call_with_prefix("self._handle", meth) %} {% endmatch %} + {% endif %} {% endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index f462604539..cd10d00f7f 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -22,6 +22,7 @@ {% include "RustBufferTemplate.py" %} {% include "RustBufferStream.py" %} {% include "RustBufferBuilder.py" %} +{% include "Helpers.py" %} # Error definitions {% include "ErrorTemplate.py" %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index 672f7b23b3..427d346796 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -1,17 +1,23 @@ public protocol {{ obj.name() }}Protocol { {% for meth in obj.methods() -%} + {%- if ! meth.is_static() %} func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%} {%- match meth.return_type() -%} {%- when Some with (return_type) %} -> {{ return_type|type_swift -}} {%- else -%} {%- endmatch %} + {%- endif %} {% endfor %} } public class {{ obj.name() }}: {{ obj.name() }}Protocol { private let handle: UInt64 + init(fromRawHandle handle: UInt64) { + self.handle = handle + } + {%- for cons in obj.constructors() %} public init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} { self.handle = {% call swift::to_ffi_call(cons) %} @@ -24,8 +30,27 @@ public class {{ obj.name() }}: {{ obj.name() }}Protocol { } } - // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) + static func lift(_ handle: UInt64) throws -> {{ obj.name()|class_name_swift }} { + {{ obj.name()|class_name_swift }}(fromRawHandle: handle) + } + + {# // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) #} {% for meth in obj.methods() -%} + {%- if meth.is_static() %} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + public static func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} -> {{ return_type|type_swift }} { + let _retval = {% call swift::to_ffi_call(meth) %} + return {% call swift::try(meth) %} {{ "_retval"|lift_swift(return_type) }} + } + + {%- when None -%} + public static func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} { + {% call swift::to_ffi_call(meth) %} + } + {%- endmatch %} + {%- else -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -39,5 +64,6 @@ public class {{ obj.name() }}: {{ obj.name() }}Protocol { {% call swift::to_ffi_call_with_prefix("self.handle", meth) %} } {%- endmatch %} + {%- endif %} {% endfor %} } \ No newline at end of file diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 6cf9a67e1b..f26b6532d6 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -112,9 +112,6 @@ impl APIConverter for weedle::namespace::NamespaceMember<'_> { impl APIConverter for weedle::namespace::OperationNamespaceMember<'_> { fn convert(&self, ci: &mut ComponentInterface) -> Result { let return_type = ci.resolve_return_type_expression(&self.return_type)?; - if let Some(Type::Object(_)) = return_type { - bail!("Objects cannot currently be returned from functions"); - } Ok(Function { name: match self.identifier { None => bail!("anonymous functions are not supported {:?}", self), diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 940de7045d..581098455e 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -253,7 +253,7 @@ impl APIConverter for weedle::interface::ConstructorInterfaceMember } } -// Represents an instance method for an object type. +// Represents a static or instance method for an object type. // // The in FFI, this will be a function whose first argument is a handle for an // instance of the corresponding object type. @@ -263,6 +263,7 @@ pub struct Method { pub(super) object_name: String, pub(super) return_type: Option, pub(super) arguments: Vec, + pub(super) static_: bool, pub(super) ffi_func: FFIFunction, pub(super) attributes: MethodAttributes, } @@ -280,19 +281,27 @@ impl Method { self.return_type.as_ref() } + pub fn is_static(&self) -> bool { + self.static_ + } + pub fn ffi_func(&self) -> &FFIFunction { &self.ffi_func } - pub fn first_argument(&self) -> Argument { - Argument { - name: "handle".to_string(), - // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it - // is contained in the proper `TypeUniverse`, but this works for now. - type_: Type::Object(self.object_name.clone()), - by_ref: false, - optional: false, - default: None, + pub fn first_argument(&self) -> Option { + if self.static_ { + None + } else { + Some(Argument { + name: "handle".to_string(), + // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it + // is contained in the proper `TypeUniverse`, but this works for now. + type_: Type::Object(self.object_name.clone()), + by_ref: false, + optional: false, + default: None, + }) } } @@ -306,7 +315,8 @@ impl Method { self.ffi_func.name.push_str(obj_prefix); self.ffi_func.name.push_str("_"); self.ffi_func.name.push_str(&self.name); - self.ffi_func.arguments = vec![self.first_argument()] + self.ffi_func.arguments = self + .first_argument() .iter() .chain(self.arguments.iter()) .map(Into::into) @@ -337,22 +347,30 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { if self.special.is_some() { bail!("special operations not supported"); } - if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier { - bail!("stringifiers are not supported"); - } + let static_ = match self.modifier { + None => false, + Some(weedle::interface::StringifierOrStatic::Static(_)) => true, + Some(weedle::interface::StringifierOrStatic::Stringifier(_)) => { + bail!("stringifiers are not supported") + } + }; let return_type = ci.resolve_return_type_expression(&self.return_type)?; - if let Some(Type::Object(_)) = return_type { - bail!("Objects cannot currently be returned from functions"); - } Ok(Method { name: match self.identifier { None => bail!("anonymous methods are not supported {:?}", self), - Some(id) => id.0.to_string(), + Some(id) => { + let name = id.0.to_string(); + if name == "new" { + bail!("the method name \"new\" is reserved for the default constructor"); + } + name + } }, // We don't know the name of the containing `Object` at this point, fill it in later. object_name: Default::default(), arguments: self.args.body.list.convert(ci)?, return_type, + static_, ffi_func: Default::default(), attributes: match &self.attributes { Some(attr) => MethodAttributes::try_from(attr)?, @@ -413,4 +431,36 @@ mod test { Ok(()) } + + #[test] + fn test_static_vs_instance_methods() -> Result<()> { + const UDL: &str = r#" + namespace test{}; + interface Testing { + u32 instance_method(); + static u32 static_method(); + }; + "#; + let ci = ComponentInterface::from_webidl(UDL).unwrap(); + assert_eq!(ci.iter_object_definitions().len(), 1); + + let obj = ci.get_object_definition("Testing").unwrap(); + assert_eq!(obj.methods().len(), 2); + + let method = obj.methods()[0]; + assert_eq!(method.name(), "instance_method"); + assert!(!method.is_static()); + assert_eq!(method.arguments.len(), 0); + assert!(method.first_argument().is_some()); + assert_eq!(method.ffi_func.arguments.len(), 1); + + let method = obj.methods()[1]; + assert_eq!(method.name(), "static_method"); + assert!(method.is_static()); + assert_eq!(method.arguments.len(), 0); + assert!(method.first_argument().is_none()); + assert_eq!(method.ffi_func.arguments.len(), 0); + + Ok(()) + } } diff --git a/uniffi_bindgen/src/templates/ObjectTemplate.rs b/uniffi_bindgen/src/templates/ObjectTemplate.rs index a8664adb54..94773094e9 100644 --- a/uniffi_bindgen/src/templates/ObjectTemplate.rs +++ b/uniffi_bindgen/src/templates/ObjectTemplate.rs @@ -48,6 +48,42 @@ uniffi::deps::lazy_static::lazy_static! { uniffi::deps::log::debug!("{{ meth.ffi_func().name() }}"); // If the method does not have the same signature as declared in the UDL, then // this attempt to call it will fail with a (somewhat) helpful compiler error. + {% if meth.is_static() %} + {% call rs::to_rs_static_method_call(obj, meth) %} + {% else %} {% call rs::to_rs_method_call(obj, meth) %} + {% endif %} } {% endfor %} + +// We proide a `ViaFfi` impl for each object, but the only thing it can currently +// do is lower an owned instance into a handle. This is useful for returning new +// instances of the object from static methods, and we know it is safe and sound +// because Rust's ownership system ensures that: +// * the thing we're operating on is an owned instance (not a reference) and +// so we know that nothing else in the program is looking at it. +// * the ownership is irrevokably transferred to the handlemap, and hence to +// the foreign language code, which takes responsibility for it from there. +// +// Other operations are not yet supported, since they would involve much more +// complicated semantics around references. +unsafe impl uniffi::ViaFfi for {{ obj.name() }} { + type FfiType = u64; + + fn lower(self) -> Self::FfiType { + // Note that this consumes `self`, transferring ownership to the handlemap. + {{ handle_map }}.insert(self).into_u64() + } + + fn try_lift(_v: Self::FfiType) -> uniffi::deps::anyhow::Result { + uniffi::deps::anyhow::bail!("Lifting object types is not yet supported"); + } + + fn write(&self, _buf: &mut B) { + panic!("Writing object types is not yet supported"); + } + + fn try_read(_buf: &mut B) -> uniffi::deps::anyhow::Result { + uniffi::deps::anyhow::bail!("Reading object types is not yet supported"); + } +} \ No newline at end of file diff --git a/uniffi_bindgen/src/templates/macros.rs b/uniffi_bindgen/src/templates/macros.rs index f708963801..0a9ab24517 100644 --- a/uniffi_bindgen/src/templates/macros.rs +++ b/uniffi_bindgen/src/templates/macros.rs @@ -65,18 +65,33 @@ use uniffi::UniffiMethodCall; {%- endif -%} {% match meth.throws() -%} {% when Some with (e) -%} -{{ this_handle_map }}.method_call_with_result(err, {{ meth.first_argument().name() }}, |obj| -> Result<{% call return_type_func(meth) %}, {{e}}> { +{{ this_handle_map }}.method_call_with_result(err, {{ meth.first_argument().unwrap().name() }}, |obj| -> Result<{% call return_type_func(meth) %}, {{e}}> { let _retval = {{ obj.name() }}::{%- call to_rs_call_with_prefix("obj", meth) -%}?; Ok({% call ret(meth) %}) }) {% else -%} -{{ this_handle_map }}.method_call_with_output(err, {{ meth.first_argument().name() }}, |obj| { +{{ this_handle_map }}.method_call_with_output(err, {{ meth.first_argument().unwrap().name() }}, |obj| { let _retval = {{ obj.name() }}::{%- call to_rs_call_with_prefix("obj", meth) -%}; {% call ret(meth) %} }) {% endmatch -%} {% endmacro -%} +{% macro to_rs_static_method_call(obj, meth) -%} +{% match meth.throws() -%} +{% when Some with (e) -%} +uniffi::deps::ffi_support::call_with_result(err, || -> Result<{% call return_type_func(meth) %}, {{e}}> { + let _retval = {{ obj.name() }}::{% call to_rs_call(meth) %}?; + Ok({% call ret(meth) %}) +}) +{% else -%} +uniffi::deps::ffi_support::call_with_output(err, || { + let _retval = {{ obj.name() }}::{% call to_rs_call(meth) %}; + {% call ret(meth) %} +}) +{% endmatch -%} +{% endmacro -%} + {% macro to_rs_function_call(func) %} {% match func.throws() %} {% when Some with (e) %}