summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/swift
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs5
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs23
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs2
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs209
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift100
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h48
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift113
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift57
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift145
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift69
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift44
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift201
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift12
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift17
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift4
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift49
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift3
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift125
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift8
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs24
26 files changed, 839 insertions, 536 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
index 5d8b37e0af..dab89e0259 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
@@ -6,21 +6,25 @@ use super::CodeType;
#[derive(Debug)]
pub struct CallbackInterfaceCodeType {
- id: String,
+ name: String,
}
impl CallbackInterfaceCodeType {
- pub fn new(id: String) -> Self {
- Self { id }
+ pub fn new(name: String) -> Self {
+ Self { name }
}
}
impl CodeType for CallbackInterfaceCodeType {
fn type_label(&self) -> String {
- super::SwiftCodeOracle.class_name(&self.id)
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
format!("CallbackInterface{}", self.type_label())
}
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some(format!("uniffiCallbackInit{}", self.name))
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
index 8e6dddf3f9..d89fdfd386 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
@@ -30,8 +30,9 @@ impl CodeType for OptionalCodeType {
fn literal(&self, literal: &Literal) -> String {
match literal {
- Literal::Null => "nil".into(),
- _ => super::SwiftCodeOracle.find(&self.inner).literal(literal),
+ Literal::None => "nil".into(),
+ Literal::Some { inner } => super::SwiftCodeOracle.find(&self.inner).literal(inner),
+ _ => panic!("Invalid literal for Optional type: {literal:?}"),
}
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
deleted file mode 100644
index b488b004cf..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/executor.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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::CodeType;
-
-#[derive(Debug)]
-pub struct ForeignExecutorCodeType;
-
-impl CodeType for ForeignExecutorCodeType {
- fn type_label(&self) -> String {
- // On Swift, we define a struct to represent a ForeignExecutor
- "UniFfiForeignExecutor".into()
- }
-
- fn canonical_name(&self) -> String {
- "ForeignExecutor".into()
- }
-
- fn initialization_fn(&self) -> Option<String> {
- Some("uniffiInitForeignExecutor".into())
- }
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
index 0b6728ba84..3960b7aae1 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/external.rs
@@ -17,7 +17,7 @@ impl ExternalCodeType {
impl CodeType for ExternalCodeType {
fn type_label(&self) -> String {
- self.name.clone()
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
index 12db4afc66..16c1625123 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
@@ -10,25 +10,49 @@ use std::fmt::Debug;
use anyhow::{Context, Result};
use askama::Template;
-use heck::{ToLowerCamelCase, ToUpperCamelCase};
+use camino::Utf8Path;
+use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use super::Bindings;
use crate::backend::TemplateExpression;
+use crate::bindings::swift;
use crate::interface::*;
-use crate::BindingsConfig;
+use crate::{BindingGenerator, BindingsConfig};
mod callback_interface;
mod compounds;
mod custom;
mod enum_;
-mod executor;
mod external;
mod miscellany;
mod object;
mod primitives;
mod record;
+pub struct SwiftBindingGenerator;
+impl BindingGenerator for SwiftBindingGenerator {
+ type Config = Config;
+
+ fn write_bindings(
+ &self,
+ ci: &ComponentInterface,
+ config: &Config,
+ out_dir: &Utf8Path,
+ try_format_code: bool,
+ ) -> Result<()> {
+ swift::write_bindings(config, ci, out_dir, try_format_code)
+ }
+
+ fn check_library_path(
+ &self,
+ _library_path: &Utf8Path,
+ _cdylib_name: Option<&str>,
+ ) -> Result<()> {
+ Ok(())
+ }
+}
+
/// A trait tor the implementation.
trait CodeType: Debug {
/// The language specific label used to reference this type. This will be used in
@@ -196,6 +220,8 @@ pub struct Config {
ffi_module_filename: Option<String>,
generate_module_map: Option<bool>,
omit_argument_labels: Option<bool>,
+ generate_immutable_records: Option<bool>,
+ experimental_sendable_value_types: Option<bool>,
#[serde(default)]
custom_types: HashMap<String, CustomTypeConfig>,
}
@@ -261,6 +287,16 @@ impl Config {
pub fn omit_argument_labels(&self) -> bool {
self.omit_argument_labels.unwrap_or(false)
}
+
+ /// Whether to generate immutable records (`let` instead of `var`)
+ pub fn generate_immutable_records(&self) -> bool {
+ self.generate_immutable_records.unwrap_or(false)
+ }
+
+ /// Whether to mark value types as 'Sendable'
+ pub fn experimental_sendable_value_types(&self) -> bool {
+ self.experimental_sendable_value_types.unwrap_or(false)
+ }
}
impl BindingsConfig for Config {
@@ -400,7 +436,6 @@ pub struct SwiftWrapper<'a> {
ci: &'a ComponentInterface,
type_helper_code: String,
type_imports: BTreeSet<String>,
- has_async_fns: bool,
}
impl<'a> SwiftWrapper<'a> {
pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
@@ -412,7 +447,6 @@ impl<'a> SwiftWrapper<'a> {
ci,
type_helper_code,
type_imports,
- has_async_fns: ci.has_async_fns(),
}
}
@@ -425,10 +459,6 @@ impl<'a> SwiftWrapper<'a> {
.iter_types()
.map(|t| SwiftCodeOracle.find(t))
.filter_map(|ct| ct.initialization_fn())
- .chain(
- self.has_async_fns
- .then(|| "uniffiInitContinuationCallback".into()),
- )
.collect()
}
}
@@ -464,12 +494,11 @@ impl SwiftCodeOracle {
Type::Duration => Box::new(miscellany::DurationCodeType),
Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
- Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Object { name, imp, .. } => Box::new(object::ObjectCodeType::new(name, imp)),
Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
Type::CallbackInterface { name, .. } => {
Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
}
- Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
Type::Optional { inner_type } => {
Box::new(compounds::OptionalCodeType::new(*inner_type))
}
@@ -509,7 +538,22 @@ impl SwiftCodeOracle {
nm.to_string().to_lower_camel_case()
}
- fn ffi_type_label_raw(&self, ffi_type: &FfiType) -> String {
+ /// Get the idiomatic Swift rendering of an FFI callback function name
+ fn ffi_callback_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of an FFI struct name
+ fn ffi_struct_name(&self, nm: &str) -> String {
+ format!("Uniffi{}", nm.to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Swift rendering of an if guard name
+ fn if_guard_name(&self, nm: &str) -> String {
+ format!("UNIFFI_FFIDEF_{}", nm.to_shouty_snake_case())
+ }
+
+ fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "Int8".into(),
FfiType::UInt8 => "UInt8".into(),
@@ -521,40 +565,74 @@ impl SwiftCodeOracle {
FfiType::UInt64 => "UInt64".into(),
FfiType::Float32 => "Float".into(),
FfiType::Float64 => "Double".into(),
+ FfiType::Handle => "UInt64".into(),
FfiType::RustArcPtr(_) => "UnsafeMutableRawPointer".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::RustCallStatus => "RustCallStatus".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
- FfiType::ForeignCallback => "ForeignCallback".into(),
- FfiType::ForeignExecutorHandle => "Int".into(),
- FfiType::ForeignExecutorCallback => "ForeignExecutorCallback".into(),
- FfiType::RustFutureContinuationCallback => "UniFfiRustFutureContinuation".into(),
- FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
- "UnsafeMutableRawPointer".into()
+ // Note: @escaping is required for Swift versions before 5.7 for callbacks passed into
+ // async functions. Swift 5.7 and later does not require it. We should probably remove
+ // it once we upgrade our minimum requirement to 5.7 or later.
+ FfiType::Callback(name) => format!("@escaping {}", self.ffi_callback_name(name)),
+ FfiType::Struct(name) => self.ffi_struct_name(name),
+ FfiType::Reference(inner) => {
+ format!("UnsafeMutablePointer<{}>", self.ffi_type_label(inner))
}
+ FfiType::VoidPointer => "UnsafeMutableRawPointer".into(),
}
}
- fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
- match ffi_type {
- FfiType::ForeignCallback
- | FfiType::ForeignExecutorCallback
- | FfiType::RustFutureHandle
- | FfiType::RustFutureContinuationCallback
- | FfiType::RustFutureContinuationData => {
- format!("{} _Nonnull", self.ffi_type_label_raw(ffi_type))
- }
- _ => self.ffi_type_label_raw(ffi_type),
+ /// Default values for FFI types
+ ///
+ /// Used to set a default return value when returning an error
+ fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String {
+ match return_type {
+ Some(t) => match t {
+ FfiType::UInt8
+ | FfiType::Int8
+ | FfiType::UInt16
+ | FfiType::Int16
+ | FfiType::UInt32
+ | FfiType::Int32
+ | FfiType::UInt64
+ | FfiType::Int64 => "0".to_owned(),
+ FfiType::Float32 | FfiType::Float64 => "0.0".to_owned(),
+ FfiType::RustArcPtr(_) => "nil".to_owned(),
+ FfiType::RustBuffer(_) => "RustBuffer.empty()".to_owned(),
+ _ => unimplemented!("FFI return type: {t:?}"),
+ },
+ // When we need to use a value for void returns, we use a `u8` placeholder
+ None => "0".to_owned(),
}
}
fn ffi_canonical_name(&self, ffi_type: &FfiType) -> String {
- self.ffi_type_label_raw(ffi_type)
+ self.ffi_type_label(ffi_type)
+ }
+
+ /// Get the name of the protocol and class name for an object.
+ ///
+ /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that.
+ /// Otherwise, the class name is the object name and the protocol name is derived from that.
+ ///
+ /// This split determines what types `FfiConverter.lower()` inputs. If we support callback
+ /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower
+ /// only lowers the concrete class.
+ fn object_names(&self, obj: &Object) -> (String, String) {
+ let class_name = self.class_name(obj.name());
+ if obj.has_callback_interface() {
+ let impl_name = format!("{class_name}Impl");
+ (class_name, impl_name)
+ } else {
+ (format!("{class_name}Protocol"), class_name)
+ }
}
}
pub mod filters {
use super::*;
pub use crate::backend::filters::*;
+ use uniffi_meta::LiteralMetadata;
fn oracle() -> &'static SwiftCodeOracle {
&SwiftCodeOracle
@@ -564,6 +642,13 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).type_label())
}
+ pub fn return_type_name(as_type: Option<&impl AsType>) -> Result<String, askama::Error> {
+ Ok(match as_type {
+ Some(as_type) => oracle().find(&as_type.as_type()).type_label(),
+ None => "()".to_owned(),
+ })
+ }
+
pub fn canonical_name(as_type: &impl AsType) -> Result<String, askama::Error> {
Ok(oracle().find(&as_type.as_type()).canonical_name())
}
@@ -572,6 +657,15 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).ffi_converter_name())
}
+ pub fn ffi_error_converter_name(as_type: &impl AsType) -> Result<String, askama::Error> {
+ // special handling for types used as errors.
+ let mut name = oracle().find(&as_type.as_type()).ffi_converter_name();
+ if matches!(&as_type.as_type(), Type::Object { .. }) {
+ name.push_str("__as_error")
+ }
+ Ok(name)
+ }
+
pub fn lower_fn(as_type: &impl AsType) -> Result<String, askama::Error> {
Ok(oracle().find(&as_type.as_type()).lower())
}
@@ -595,6 +689,16 @@ pub mod filters {
Ok(oracle().find(&as_type.as_type()).literal(literal))
}
+ // Get the idiomatic Swift rendering of an individual enum variant's discriminant
+ pub fn variant_discr_literal(e: &Enum, index: &usize) -> Result<String, askama::Error> {
+ let literal = e.variant_discr(*index).expect("invalid index");
+ match literal {
+ LiteralMetadata::UInt(v, _, _) => Ok(v.to_string()),
+ LiteralMetadata::Int(v, _, _) => Ok(v.to_string()),
+ _ => unreachable!("expected an UInt!"),
+ }
+ }
+
/// Get the Swift type for an FFIType
pub fn ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
Ok(oracle().ffi_type_label(ffi_type))
@@ -604,6 +708,10 @@ pub mod filters {
Ok(oracle().ffi_canonical_name(ffi_type))
}
+ pub fn ffi_default_value(return_type: Option<FfiType>) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_default_value(return_type.as_ref()))
+ }
+
/// Like `ffi_type_name`, but used in `BridgingHeaderTemplate.h` which uses a slightly different
/// names.
pub fn header_ffi_type_name(ffi_type: &FfiType) -> Result<String, askama::Error> {
@@ -618,18 +726,17 @@ pub mod filters {
FfiType::UInt64 => "uint64_t".into(),
FfiType::Float32 => "float".into(),
FfiType::Float64 => "double".into(),
+ FfiType::Handle => "uint64_t".into(),
FfiType::RustArcPtr(_) => "void*_Nonnull".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
+ FfiType::RustCallStatus => "RustCallStatus".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
- FfiType::ForeignCallback => "ForeignCallback _Nonnull".into(),
- FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback _Nonnull".into(),
- FfiType::ForeignExecutorHandle => "size_t".into(),
- FfiType::RustFutureContinuationCallback => {
- "UniFfiRustFutureContinuation _Nonnull".into()
- }
- FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
- "void* _Nonnull".into()
+ FfiType::Callback(name) => {
+ format!("{} _Nonnull", SwiftCodeOracle.ffi_callback_name(name))
}
+ FfiType::Struct(name) => SwiftCodeOracle.ffi_struct_name(name),
+ FfiType::Reference(inner) => format!("{}* _Nonnull", header_ffi_type_name(inner)?),
+ FfiType::VoidPointer => "void* _Nonnull".into(),
})
}
@@ -664,6 +771,30 @@ pub mod filters {
Ok(oracle().enum_variant_name(nm))
}
+ /// Get the idiomatic Swift rendering of an FFI callback function name
+ pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_callback_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of an FFI struct name
+ pub fn ffi_struct_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().ffi_struct_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of an if guard name
+ pub fn if_guard_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(oracle().if_guard_name(nm))
+ }
+
+ /// Get the idiomatic Swift rendering of docstring
+ pub fn docstring(docstring: &str, spaces: &i32) -> Result<String, askama::Error> {
+ let middle = textwrap::indent(&textwrap::dedent(docstring), " * ");
+ let wrapped = format!("/**\n{middle}\n */");
+
+ let spaces = usize::try_from(*spaces).unwrap_or_default();
+ Ok(textwrap::indent(&wrapped, &" ".repeat(spaces)))
+ }
+
pub fn error_handler(result: &ResultType) -> Result<String, askama::Error> {
Ok(match &result.throws_type {
Some(t) => format!("{}.lift", ffi_converter_name(t)?),
@@ -685,4 +816,8 @@ pub mod filters {
}
))
}
+
+ pub fn object_names(obj: &Object) -> Result<(String, String), askama::Error> {
+ Ok(SwiftCodeOracle.object_names(obj))
+ }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
index ea140c998d..d4497a7b19 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/object.rs
@@ -3,24 +3,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::CodeType;
+use crate::interface::ObjectImpl;
#[derive(Debug)]
pub struct ObjectCodeType {
- id: String,
+ name: String,
+ imp: ObjectImpl,
}
impl ObjectCodeType {
- pub fn new(id: String) -> Self {
- Self { id }
+ pub fn new(name: String, imp: ObjectImpl) -> Self {
+ Self { name, imp }
}
}
impl CodeType for ObjectCodeType {
fn type_label(&self) -> String {
- super::SwiftCodeOracle.class_name(&self.id)
+ super::SwiftCodeOracle.class_name(&self.name)
}
fn canonical_name(&self) -> String {
- format!("Type{}", self.id)
+ format!("Type{}", self.name)
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ self.imp
+ .has_callback_interface()
+ .then(|| format!("uniffiCallbackInit{}", self.name))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
index 86424658a3..e0c670520e 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/gen_swift/primitives.rs
@@ -9,7 +9,11 @@ use paste::paste;
fn render_literal(literal: &Literal) -> String {
fn typed_number(type_: &Type, num_str: String) -> String {
- match type_ {
+ let unwrapped_type = match type_ {
+ Type::Optional { inner_type } => inner_type,
+ t => t,
+ };
+ match unwrapped_type {
// special case Int32.
Type::Int32 => num_str,
// otherwise use constructor e.g. UInt8(x)
@@ -29,7 +33,7 @@ fn render_literal(literal: &Literal) -> String {
super::SwiftCodeOracle.find(type_).type_label()
)
}
- _ => panic!("Unexpected literal: {num_str} is not a number"),
+ _ => panic!("Unexpected literal: {num_str} for type: {type_:?}"),
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
index 695208861d..e16f3108e1 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Async.swift
@@ -1,11 +1,13 @@
private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0
private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1
+fileprivate let uniffiContinuationHandleMap = UniffiHandleMap<UnsafeContinuation<Int8, Never>>()
+
fileprivate func uniffiRustCallAsync<F, T>(
- rustFutureFunc: () -> UnsafeMutableRawPointer,
- pollFunc: (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> (),
- completeFunc: (UnsafeMutableRawPointer, UnsafeMutablePointer<RustCallStatus>) -> F,
- freeFunc: (UnsafeMutableRawPointer) -> (),
+ rustFutureFunc: () -> UInt64,
+ pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (),
+ completeFunc: (UInt64, UnsafeMutablePointer<RustCallStatus>) -> F,
+ freeFunc: (UInt64) -> (),
liftFunc: (F) throws -> T,
errorHandler: ((RustBuffer) throws -> Error)?
) async throws -> T {
@@ -19,7 +21,11 @@ fileprivate func uniffiRustCallAsync<F, T>(
var pollResult: Int8;
repeat {
pollResult = await withUnsafeContinuation {
- pollFunc(rustFuture, ContinuationHolder($0).toOpaque())
+ pollFunc(
+ rustFuture,
+ uniffiFutureContinuationCallback,
+ uniffiContinuationHandleMap.insert(obj: $0)
+ )
}
} while pollResult != UNIFFI_RUST_FUTURE_POLL_READY
@@ -31,32 +37,80 @@ fileprivate func uniffiRustCallAsync<F, T>(
// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They
// lift the return value or error and resume the suspended function.
-fileprivate func uniffiFutureContinuationCallback(ptr: UnsafeMutableRawPointer, pollResult: Int8) {
- ContinuationHolder.fromOpaque(ptr).resume(pollResult)
+fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) {
+ if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) {
+ continuation.resume(returning: pollResult)
+ } else {
+ print("uniffiFutureContinuationCallback invalid handle")
+ }
}
-// Wraps UnsafeContinuation in a class so that we can use reference counting when passing it across
-// the FFI
-fileprivate class ContinuationHolder {
- let continuation: UnsafeContinuation<Int8, Never>
-
- init(_ continuation: UnsafeContinuation<Int8, Never>) {
- self.continuation = continuation
+{%- if ci.has_async_callback_interface_definition() %}
+private func uniffiTraitInterfaceCallAsync<T>(
+ makeCall: @escaping () async throws -> T,
+ handleSuccess: @escaping (T) -> (),
+ handleError: @escaping (Int8, RustBuffer) -> ()
+) -> UniffiForeignFuture {
+ let task = Task {
+ do {
+ handleSuccess(try await makeCall())
+ } catch {
+ handleError(CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(String(describing: error)))
+ }
}
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
+ return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree)
- func resume(_ pollResult: Int8) {
- self.continuation.resume(returning: pollResult)
- }
+}
- func toOpaque() -> UnsafeMutableRawPointer {
- return Unmanaged<ContinuationHolder>.passRetained(self).toOpaque()
+private func uniffiTraitInterfaceCallAsyncWithError<T, E>(
+ makeCall: @escaping () async throws -> T,
+ handleSuccess: @escaping (T) -> (),
+ handleError: @escaping (Int8, RustBuffer) -> (),
+ lowerError: @escaping (E) -> RustBuffer
+) -> UniffiForeignFuture {
+ let task = Task {
+ do {
+ handleSuccess(try await makeCall())
+ } catch let error as E {
+ handleError(CALL_ERROR, lowerError(error))
+ } catch {
+ handleError(CALL_UNEXPECTED_ERROR, {{ Type::String.borrow()|lower_fn }}(String(describing: error)))
+ }
}
+ let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task)
+ return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree)
+}
+
+// Borrow the callback handle map implementation to store foreign future handles
+// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823)
+fileprivate var UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap<UniffiForeignFutureTask>()
+
+// Protocol for tasks that handle foreign futures.
+//
+// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done
+// with the task object itself, since has generic parameters.
+protocol UniffiForeignFutureTask {
+ func cancel()
+}
+
+extension Task: UniffiForeignFutureTask {}
- static func fromOpaque(_ ptr: UnsafeRawPointer) -> ContinuationHolder {
- return Unmanaged<ContinuationHolder>.fromOpaque(ptr).takeRetainedValue()
+private func uniffiForeignFutureFree(handle: UInt64) {
+ do {
+ let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle)
+ // Set the cancellation flag on the task. If it's still running, the code can check the
+ // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is
+ // a no-op.
+ task.cancel()
+ } catch {
+ print("uniffiForeignFutureFree: handle missing from handlemap")
}
}
-fileprivate func uniffiInitContinuationCallback() {
- {{ ci.ffi_rust_future_continuation_callback_set().name() }}(uniffiFutureContinuationCallback)
+// For testing
+public func uniffiForeignFutureHandleCount{{ ci.namespace()|class_name }}() -> Int {
+ UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count
}
+
+{%- endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
index 87698e359f..89d98594d3 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/BridgingHeaderTemplate.h
@@ -24,25 +24,11 @@
typedef struct RustBuffer
{
- int32_t capacity;
- int32_t len;
+ uint64_t capacity;
+ uint64_t len;
uint8_t *_Nullable data;
} RustBuffer;
-typedef int32_t (*ForeignCallback)(uint64_t, int32_t, const uint8_t *_Nonnull, int32_t, RustBuffer *_Nonnull);
-
-// Task defined in Rust that Swift executes
-typedef void (*UniFfiRustTaskCallback)(const void * _Nullable, int8_t);
-
-// Callback to execute Rust tasks using a Swift Task
-//
-// Args:
-// executor: ForeignExecutor lowered into a size_t value
-// delay: Delay in MS
-// task: UniFfiRustTaskCallback to call
-// task_data: data to pass the task callback
-typedef int8_t (*UniFfiForeignExecutorCallback)(size_t, uint32_t, UniFfiRustTaskCallback _Nullable, const void * _Nullable);
-
typedef struct ForeignBytes
{
int32_t len;
@@ -59,11 +45,29 @@ typedef struct RustCallStatus {
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
#endif // def UNIFFI_SHARED_H
-// Continuation callback for UniFFI Futures
-typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
-
-// Scaffolding functions
-{%- for func in ci.iter_ffi_function_definitions() %}
+{%- for def in ci.ffi_definitions() %}
+#ifndef {{ def.name()|if_guard_name }}
+#define {{ def.name()|if_guard_name }}
+{%- match def %}
+{% when FfiDefinition::CallbackFunction(callback) %}
+typedef
+ {%- match callback.return_type() %}{% when Some(return_type) %} {{ return_type|header_ffi_type_name }} {% when None %} void {% endmatch -%}
+ (*{{ callback.name()|ffi_callback_name }})(
+ {%- for arg in callback.arguments() -%}
+ {{ arg.type_().borrow()|header_ffi_type_name }}
+ {%- if !loop.last || callback.has_rust_call_status_arg() %}, {% endif %}
+ {%- endfor -%}
+ {%- if callback.has_rust_call_status_arg() %}
+ RustCallStatus *_Nonnull uniffiCallStatus
+ {%- endif %}
+ );
+{% when FfiDefinition::Struct(struct) %}
+typedef struct {{ struct.name()|ffi_struct_name }} {
+ {%- for field in struct.fields() %}
+ {{ field.type_().borrow()|header_ffi_type_name }} {{ field.name()|var_name }};
+ {%- endfor %}
+} {{ struct.name()|ffi_struct_name }};
+{% when FfiDefinition::Function(func) %}
{% match func.return_type() -%}{%- when Some with (type_) %}{{ type_|header_ffi_type_name }}{% when None %}void{% endmatch %} {{ func.name() }}(
{%- if func.arguments().len() > 0 %}
{%- for arg in func.arguments() %}
@@ -74,6 +78,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
{%- if func.has_rust_call_status_arg() %}RustCallStatus *_Nonnull out_status{%- else %}void{% endif %}
{% endif %}
);
+{%- endmatch %}
+#endif
{%- endfor %}
{% import "macros.swift" as swift %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift
new file mode 100644
index 0000000000..74ee372642
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceImpl.swift
@@ -0,0 +1,113 @@
+{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
+{%- let trait_impl=format!("UniffiCallbackInterface{}", name) %}
+
+// Put the implementation in a struct so we don't pollute the top-level namespace
+fileprivate struct {{ trait_impl }} {
+
+ // Create the VTable using a series of closures.
+ // Swift automatically converts these into C callback functions.
+ static var vtable: {{ vtable|ffi_type_name }} = {{ vtable|ffi_type_name }}(
+ {%- for (ffi_callback, meth) in vtable_methods %}
+ {{ meth.name()|fn_name }}: { (
+ {%- for arg in ffi_callback.arguments() %}
+ {{ arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name }}{% if !loop.last || ffi_callback.has_rust_call_status_arg() %},{% endif %}
+ {%- endfor -%}
+ {%- if ffi_callback.has_rust_call_status_arg() %}
+ uniffiCallStatus: UnsafeMutablePointer<RustCallStatus>
+ {%- endif %}
+ ) in
+ let makeCall = {
+ () {% if meth.is_async() %}async {% endif %}throws -> {% match meth.return_type() %}{% when Some(t) %}{{ t|type_name }}{% when None %}(){% endmatch %} in
+ guard let uniffiObj = try? {{ ffi_converter_name }}.handleMap.get(handle: uniffiHandle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return {% if meth.throws() %}try {% endif %}{% if meth.is_async() %}await {% endif %}uniffiObj.{{ meth.name()|fn_name }}(
+ {%- for arg in meth.arguments() %}
+ {% if !config.omit_argument_labels() %} {{ arg.name()|arg_name }}: {% endif %}try {{ arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
+ {%- endfor %}
+ )
+ }
+ {%- if !meth.is_async() %}
+
+ {% match meth.return_type() %}
+ {%- when Some(t) %}
+ let writeReturn = { uniffiOutReturn.pointee = {{ t|lower_fn }}($0) }
+ {%- when None %}
+ let writeReturn = { () }
+ {%- endmatch %}
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ uniffiTraitInterfaceCall(
+ callStatus: uniffiCallStatus,
+ makeCall: makeCall,
+ writeReturn: writeReturn
+ )
+ {%- when Some(error_type) %}
+ uniffiTraitInterfaceCallWithError(
+ callStatus: uniffiCallStatus,
+ makeCall: makeCall,
+ writeReturn: writeReturn,
+ lowerError: {{ error_type|lower_fn }}
+ )
+ {%- endmatch %}
+ {%- else %}
+
+ let uniffiHandleSuccess = { (returnValue: {{ meth.return_type()|return_type_name }}) in
+ uniffiFutureCallback(
+ uniffiCallbackData,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ returnValue: {{ return_type|lower_fn }}(returnValue),
+ {%- when None %}
+ {%- endmatch %}
+ callStatus: RustCallStatus()
+ )
+ )
+ }
+ let uniffiHandleError = { (statusCode, errorBuf) in
+ uniffiFutureCallback(
+ uniffiCallbackData,
+ {{ meth.foreign_future_ffi_result_struct().name()|ffi_struct_name }}(
+ {%- match meth.return_type() %}
+ {%- when Some(return_type) %}
+ returnValue: {{ meth.return_type().map(FfiType::from)|ffi_default_value }},
+ {%- when None %}
+ {%- endmatch %}
+ callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf)
+ )
+ )
+ }
+
+ {%- match meth.throws_type() %}
+ {%- when None %}
+ let uniffiForeignFuture = uniffiTraitInterfaceCallAsync(
+ makeCall: makeCall,
+ handleSuccess: uniffiHandleSuccess,
+ handleError: uniffiHandleError
+ )
+ {%- when Some(error_type) %}
+ let uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError(
+ makeCall: makeCall,
+ handleSuccess: uniffiHandleSuccess,
+ handleError: uniffiHandleError,
+ lowerError: {{ error_type|lower_fn }}
+ )
+ {%- endmatch %}
+ uniffiOutReturn.pointee = uniffiForeignFuture
+ {%- endif %}
+ },
+ {%- endfor %}
+ uniffiFree: { (uniffiHandle: UInt64) -> () in
+ let result = try? {{ ffi_converter_name }}.handleMap.remove(handle: uniffiHandle)
+ if result == nil {
+ print("Uniffi callback interface {{ name }}: handle missing in uniffiFree")
+ }
+ }
+ )
+}
+
+private func {{ callback_init }}() {
+ {{ ffi_init_callback.name() }}(&{{ trait_impl }}.vtable)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
index 9ae62d1667..5863c2ad41 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceRuntime.swift
@@ -1,60 +1,3 @@
-fileprivate extension NSLock {
- func withLock<T>(f: () throws -> T) rethrows -> T {
- self.lock()
- defer { self.unlock() }
- return try f()
- }
-}
-
-fileprivate typealias UniFFICallbackHandle = UInt64
-fileprivate class UniFFICallbackHandleMap<T> {
- private var leftMap: [UniFFICallbackHandle: T] = [:]
- private var counter: [UniFFICallbackHandle: UInt64] = [:]
- private var rightMap: [ObjectIdentifier: UniFFICallbackHandle] = [:]
-
- private let lock = NSLock()
- private var currentHandle: UniFFICallbackHandle = 0
- private let stride: UniFFICallbackHandle = 1
-
- func insert(obj: T) -> UniFFICallbackHandle {
- lock.withLock {
- let id = ObjectIdentifier(obj as AnyObject)
- let handle = rightMap[id] ?? {
- currentHandle += stride
- let handle = currentHandle
- leftMap[handle] = obj
- rightMap[id] = handle
- return handle
- }()
- counter[handle] = (counter[handle] ?? 0) + 1
- return handle
- }
- }
-
- func get(handle: UniFFICallbackHandle) -> T? {
- lock.withLock {
- leftMap[handle]
- }
- }
-
- func delete(handle: UniFFICallbackHandle) {
- remove(handle: handle)
- }
-
- @discardableResult
- func remove(handle: UniFFICallbackHandle) -> T? {
- lock.withLock {
- defer { counter[handle] = (counter[handle] ?? 1) - 1 }
- guard counter[handle] == 1 else { return leftMap[handle] }
- let obj = leftMap.removeValue(forKey: handle)
- if let obj = obj {
- rightMap.removeValue(forKey: ObjectIdentifier(obj as AnyObject))
- }
- return obj
- }
- }
-}
-
// Magic number for the Rust proxy to call using the same mechanism as every other method,
// to free the callback once it's dropped by Rust.
private let IDX_CALLBACK_FREE: Int32 = 0
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
index aec8ded930..7aa1cca9b2 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/CallbackInterfaceTemplate.swift
@@ -1,150 +1,39 @@
{%- let cbi = ci|get_callback_interface_definition(name) %}
-{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
-{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
-
-// Declaration and FfiConverters for {{ type_name }} Callback Interface
-
-public protocol {{ type_name }} : AnyObject {
- {% for meth in cbi.methods() -%}
- func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
- {%- else -%}
- {%- endmatch %}
- {% endfor %}
-}
-
-// The ForeignCallback that is passed to Rust.
-fileprivate let {{ foreign_callback }} : ForeignCallback =
- { (handle: UniFFICallbackHandle, method: Int32, argsData: UnsafePointer<UInt8>, argsLen: Int32, out_buf: UnsafeMutablePointer<RustBuffer>) -> Int32 in
- {% for meth in cbi.methods() -%}
- {%- let method_name = format!("invoke_{}", meth.name())|fn_name %}
-
- func {{ method_name }}(_ swiftCallbackInterface: {{ type_name }}, _ argsData: UnsafePointer<UInt8>, _ argsLen: Int32, _ out_buf: UnsafeMutablePointer<RustBuffer>) throws -> Int32 {
- {%- if meth.arguments().len() > 0 %}
- var reader = createReader(data: Data(bytes: argsData, count: Int(argsLen)))
- {%- endif %}
-
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- func makeCall() throws -> Int32 {
- let result = {% if meth.throws() %} try{% endif %} swiftCallbackInterface.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- var writer = [UInt8]()
- {{ return_type|write_fn }}(result, into: &writer)
- out_buf.pointee = RustBuffer(bytes: writer)
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- when None %}
- func makeCall() throws -> Int32 {
- try swiftCallbackInterface.{{ meth.name()|fn_name }}(
- {% for arg in meth.arguments() -%}
- {% if !config.omit_argument_labels() %}{{ arg.name()|var_name }}: {% endif %} try {{ arg|read_fn }}(from: &reader)
- {%- if !loop.last %}, {% endif %}
- {% endfor -%}
- )
- return UNIFFI_CALLBACK_SUCCESS
- }
- {%- endmatch %}
-
- {%- match meth.throws_type() %}
- {%- when None %}
- return try makeCall()
- {%- when Some(error_type) %}
- do {
- return try makeCall()
- } catch let error as {{ error_type|type_name }} {
- out_buf.pointee = {{ error_type|lower_fn }}(error)
- return UNIFFI_CALLBACK_ERROR
- }
- {%- endmatch %}
- }
- {%- endfor %}
-
-
- switch method {
- case IDX_CALLBACK_FREE:
- {{ ffi_converter_name }}.drop(handle: handle)
- // Sucessful return
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return UNIFFI_CALLBACK_SUCCESS
- {% for meth in cbi.methods() -%}
- {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
- case {{ loop.index }}:
- let cb: {{ cbi|type_name }}
- do {
- cb = try {{ ffi_converter_name }}.lift(handle)
- } catch {
- out_buf.pointee = {{ Type::String.borrow()|lower_fn }}("{{ cbi.name() }}: Invalid handle")
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- do {
- return try {{ method_name }}(cb, argsData, argsLen, out_buf)
- } catch let error {
- out_buf.pointee = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
- {% endfor %}
- // This should never happen, because an out of bounds method index won't
- // ever be used. Once we can catch errors, we should return an InternalError.
- // https://github.com/mozilla/uniffi-rs/issues/351
- default:
- // An unexpected error happened.
- // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
- return UNIFFI_CALLBACK_UNEXPECTED_ERROR
- }
-}
+{%- let callback_handler = format!("uniffiCallbackHandler{}", name) %}
+{%- let callback_init = format!("uniffiCallbackInit{}", name) %}
+{%- let methods = cbi.methods() %}
+{%- let protocol_name = type_name.clone() %}
+{%- let protocol_docstring = cbi.docstring() %}
+{%- let vtable = cbi.vtable() %}
+{%- let vtable_methods = cbi.vtable_methods() %}
+{%- let ffi_init_callback = cbi.ffi_init_callback() %}
+
+{% include "Protocol.swift" %}
+{% include "CallbackInterfaceImpl.swift" %}
// FfiConverter protocol for callback interfaces
fileprivate struct {{ ffi_converter_name }} {
- private static let initCallbackOnce: () = {
- // Swift ensures this initializer code will once run once, even when accessed by multiple threads.
- try! rustCall { (err: UnsafeMutablePointer<RustCallStatus>) in
- {{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err)
- }
- }()
-
- private static func ensureCallbackinitialized() {
- _ = initCallbackOnce
- }
-
- static func drop(handle: UniFFICallbackHandle) {
- handleMap.remove(handle: handle)
- }
-
- private static var handleMap = UniFFICallbackHandleMap<{{ type_name }}>()
+ fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>()
}
extension {{ ffi_converter_name }} : FfiConverter {
typealias SwiftType = {{ type_name }}
- // We can use Handle as the FfiType because it's a typealias to UInt64
- typealias FfiType = UniFFICallbackHandle
+ typealias FfiType = UInt64
- public static func lift(_ handle: UniFFICallbackHandle) throws -> SwiftType {
- ensureCallbackinitialized();
- guard let callback = handleMap.get(handle: handle) else {
- throw UniffiInternalError.unexpectedStaleHandle
- }
- return callback
+ public static func lift(_ handle: UInt64) throws -> SwiftType {
+ try handleMap.get(handle: handle)
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
- ensureCallbackinitialized();
- let handle: UniFFICallbackHandle = try readInt(&buf)
+ let handle: UInt64 = try readInt(&buf)
return try lift(handle)
}
- public static func lower(_ v: SwiftType) -> UniFFICallbackHandle {
- ensureCallbackinitialized();
+ public static func lower(_ v: SwiftType) -> UInt64 {
return handleMap.insert(obj: v)
}
public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
- ensureCallbackinitialized();
writeInt(&buf, lower(v))
}
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
index 99f45290cc..1d8b3cf500 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift
@@ -1,10 +1,26 @@
// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
+{%- call swift::docstring(e, 0) %}
+{% match e.variant_discr_type() %}
+{% when None %}
public enum {{ type_name }} {
{% for variant in e.variants() %}
- case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
{% endfor %}
}
+{% when Some with (variant_discr_type) %}
+public enum {{ type_name }} : {{ variant_discr_type|type_name }} {
+ {% for variant in e.variants() %}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|enum_variant_swift_quoted }} = {{ e|variant_discr_literal(loop.index0) }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
+ {% endfor %}
+}
+{% endmatch %}
public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
@@ -15,7 +31,11 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
{% for variant in e.variants() %}
case {{ loop.index }}: return .{{ variant.name()|enum_variant_swift_quoted }}{% if variant.has_fields() %}(
{%- for field in variant.fields() %}
+ {%- if variant.has_nameless_fields() -%}
+ try {{ field|read_fn }}(from: &buf)
+ {%- else -%}
{{ field.name()|arg_name }}: try {{ field|read_fn }}(from: &buf)
+ {%- endif -%}
{%- if !loop.last %}, {% endif %}
{%- endfor %}
){%- endif %}
@@ -28,10 +48,10 @@ public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
switch value {
{% for variant in e.variants() %}
{% if variant.has_fields() %}
- case let .{{ variant.name()|enum_variant_swift_quoted }}({% for field in variant.fields() %}{{ field.name()|var_name }}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
+ case let .{{ variant.name()|enum_variant_swift_quoted }}({% for field in variant.fields() %}{%- call swift::field_name(field, loop.index) -%}{%- if loop.last -%}{%- else -%},{%- endif -%}{% endfor %}):
writeInt(&buf, Int32({{ loop.index }}))
{% for field in variant.fields() -%}
- {{ field|write_fn }}({{ field.name()|var_name }}, into: &buf)
+ {{ field|write_fn }}({% call swift::field_name(field, loop.index) %}, into: &buf)
{% endfor -%}
{% else %}
case .{{ variant.name()|enum_variant_swift_quoted }}:
@@ -55,5 +75,6 @@ public func {{ ffi_converter_name }}_lower(_ value: {{ type_name }}) -> RustBuff
}
{% if !contains_object_references %}
+{% if config.experimental_sendable_value_types() %}extension {{ type_name }}: Sendable {} {% endif %}
extension {{ type_name }}: Equatable, Hashable {}
{% endif %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
index 786091395b..0702c477e9 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ErrorTemplate.swift
@@ -1,21 +1,21 @@
+{%- call swift::docstring(e, 0) %}
public enum {{ type_name }} {
{% if e.is_flat() %}
{% for variant in e.variants() %}
- // Simple error enums only carry a message
+ {%- call swift::docstring(variant, 4) %}
case {{ variant.name()|class_name }}(message: String)
{% endfor %}
{%- else %}
{% for variant in e.variants() %}
- case {{ variant.name()|class_name }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
+ {%- call swift::docstring(variant, 4) %}
+ case {{ variant.name()|class_name }}{% if variant.fields().len() > 0 %}(
+ {%- call swift::field_list_decl(variant, variant.has_nameless_fields()) %}
+ ){% endif -%}
{% endfor %}
{%- endif %}
-
- fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error {
- return try {{ ffi_converter_name }}.lift(error)
- }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
deleted file mode 100644
index 167e4c7546..0000000000
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ForeignExecutorTemplate.swift
+++ /dev/null
@@ -1,69 +0,0 @@
-private let UNIFFI_RUST_TASK_CALLBACK_SUCCESS: Int8 = 0
-private let UNIFFI_RUST_TASK_CALLBACK_CANCELLED: Int8 = 1
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS: Int8 = 0
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELED: Int8 = 1
-private let UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR: Int8 = 2
-
-// Encapsulates an executor that can run Rust tasks
-//
-// On Swift, `Task.detached` can handle this we just need to know what priority to send it.
-public struct UniFfiForeignExecutor {
- var priority: TaskPriority
-
- public init(priority: TaskPriority) {
- self.priority = priority
- }
-
- public init() {
- self.priority = Task.currentPriority
- }
-}
-
-fileprivate struct FfiConverterForeignExecutor: FfiConverter {
- typealias SwiftType = UniFfiForeignExecutor
- // Rust uses a pointer to represent the FfiConverterForeignExecutor, but we only need a u8.
- // let's use `Int`, which is equivalent to `size_t`
- typealias FfiType = Int
-
- public static func lift(_ value: FfiType) throws -> SwiftType {
- UniFfiForeignExecutor(priority: TaskPriority(rawValue: numericCast(value)))
- }
- public static func lower(_ value: SwiftType) -> FfiType {
- numericCast(value.priority.rawValue)
- }
-
- public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
- fatalError("FfiConverterForeignExecutor.read not implemented yet")
- }
- public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
- fatalError("FfiConverterForeignExecutor.read not implemented yet")
- }
-}
-
-
-fileprivate func uniffiForeignExecutorCallback(executorHandle: Int, delayMs: UInt32, rustTask: UniFfiRustTaskCallback?, taskData: UnsafeRawPointer?) -> Int8 {
- if let rustTask = rustTask {
- let executor = try! FfiConverterForeignExecutor.lift(executorHandle)
- Task.detached(priority: executor.priority) {
- if delayMs != 0 {
- let nanoseconds: UInt64 = numericCast(delayMs * 1000000)
- try! await Task.sleep(nanoseconds: nanoseconds)
- }
- rustTask(taskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS)
- }
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- } else {
- // When rustTask is null, we should drop the foreign executor.
- // However, since its just a value type, we don't need to do anything here.
- return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS
- }
-}
-
-fileprivate func uniffiInitForeignExecutor() {
- {%- match ci.ffi_foreign_executor_callback_set() %}
- {%- when Some with (fn) %}
- {{ fn.name() }}(uniffiForeignExecutorCallback)
- {%- when None %}
- {#- No foreign executor, we don't set anything #}
- {% endmatch %}
-}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift
new file mode 100644
index 0000000000..6de9f085d6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/HandleMap.swift
@@ -0,0 +1,40 @@
+fileprivate class UniffiHandleMap<T> {
+ private var map: [UInt64: T] = [:]
+ private let lock = NSLock()
+ private var currentHandle: UInt64 = 1
+
+ func insert(obj: T) -> UInt64 {
+ lock.withLock {
+ let handle = currentHandle
+ currentHandle += 1
+ map[handle] = obj
+ return handle
+ }
+ }
+
+ func get(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map[handle] else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ @discardableResult
+ func remove(handle: UInt64) throws -> T {
+ try lock.withLock {
+ guard let obj = map.removeValue(forKey: handle) else {
+ throw UniffiInternalError.unexpectedStaleHandle
+ }
+ return obj
+ }
+ }
+
+ var count: Int {
+ get {
+ map.count
+ }
+ }
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
index a34b128e23..cfddf7b313 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Helpers.swift
@@ -26,9 +26,17 @@ fileprivate enum UniffiInternalError: LocalizedError {
}
}
+fileprivate extension NSLock {
+ func withLock<T>(f: () throws -> T) rethrows -> T {
+ self.lock()
+ defer { self.unlock() }
+ return try f()
+ }
+}
+
fileprivate let CALL_SUCCESS: Int8 = 0
fileprivate let CALL_ERROR: Int8 = 1
-fileprivate let CALL_PANIC: Int8 = 2
+fileprivate let CALL_UNEXPECTED_ERROR: Int8 = 2
fileprivate let CALL_CANCELLED: Int8 = 3
fileprivate extension RustCallStatus {
@@ -81,7 +89,7 @@ private func uniffiCheckCallStatus(
throw UniffiInternalError.unexpectedRustCallError
}
- case CALL_PANIC:
+ case CALL_UNEXPECTED_ERROR:
// When the rust code sees a panic, it tries to construct a RustBuffer
// with the message. But if that code panics, then it just sends back
// an empty buffer.
@@ -93,9 +101,39 @@ private func uniffiCheckCallStatus(
}
case CALL_CANCELLED:
- throw CancellationError()
+ fatalError("Cancellation not supported yet")
default:
throw UniffiInternalError.unexpectedRustCallStatusCode
}
}
+
+private func uniffiTraitInterfaceCall<T>(
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> ()
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ }
+}
+
+private func uniffiTraitInterfaceCallWithError<T, E>(
+ callStatus: UnsafeMutablePointer<RustCallStatus>,
+ makeCall: () throws -> T,
+ writeReturn: (T) -> (),
+ lowerError: (E) -> RustBuffer
+) {
+ do {
+ try writeReturn(makeCall())
+ } catch let error as E {
+ callStatus.pointee.code = CALL_ERROR
+ callStatus.pointee.errorBuf = lowerError(error)
+ } catch {
+ callStatus.pointee.code = CALL_UNEXPECTED_ERROR
+ callStatus.pointee.errorBuf = {{ Type::String.borrow()|lower_fn }}(String(describing: error))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
index 57a77ca6df..0c28bc4c09 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
@@ -1,104 +1,146 @@
{%- let obj = ci|get_object_definition(name) %}
-public protocol {{ obj.name() }}Protocol {
- {% for meth in obj.methods() -%}
- func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) %} {% call swift::throws(meth) -%}
- {%- match meth.return_type() -%}
- {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
- {%- else -%}
- {%- endmatch %}
- {% endfor %}
-}
-
-public class {{ type_name }}: {{ obj.name() }}Protocol {
- fileprivate let pointer: UnsafeMutableRawPointer
+{%- let (protocol_name, impl_class_name) = obj|object_names %}
+{%- let methods = obj.methods() %}
+{%- let protocol_docstring = obj.docstring() %}
+
+{%- let is_error = ci.is_name_used_as_error(name) %}
+
+{% include "Protocol.swift" %}
+
+{%- call swift::docstring(obj, 0) %}
+open class {{ impl_class_name }}:
+ {%- for tm in obj.uniffi_traits() %}
+ {%- match tm %}
+ {%- when UniffiTrait::Display { fmt } %}
+ CustomStringConvertible,
+ {%- when UniffiTrait::Debug { fmt } %}
+ CustomDebugStringConvertible,
+ {%- when UniffiTrait::Eq { eq, ne } %}
+ Equatable,
+ {%- when UniffiTrait::Hash { hash } %}
+ Hashable,
+ {%- else %}
+ {%- endmatch %}
+ {%- endfor %}
+ {%- if is_error %}
+ Error,
+ {% endif %}
+ {{ protocol_name }} {
+ fileprivate let pointer: UnsafeMutableRawPointer!
+
+ /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
+ public struct NoPointer {
+ public init() {}
+ }
// TODO: We'd like this to be `private` but for Swifty reasons,
// we can't implement `FfiConverter` without making this `required` and we can't
// make it `required` without making it `public`.
- required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
+ required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
self.pointer = pointer
}
+ /// This constructor can be used to instantiate a fake object.
+ /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
+ ///
+ /// - Warning:
+ /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash.
+ public init(noPointer: NoPointer) {
+ self.pointer = nil
+ }
+
+ public func uniffiClonePointer() -> UnsafeMutableRawPointer {
+ return try! rustCall { {{ obj.ffi_object_clone().name() }}(self.pointer, $0) }
+ }
+
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
- public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} {
- self.init(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
- }
+ {%- call swift::ctor_decl(cons, 4) %}
{%- when None %}
+ // No primary constructor declared for this class.
{%- endmatch %}
deinit {
+ guard let pointer = pointer else {
+ return
+ }
+
try! rustCall { {{ obj.ffi_object_free().name() }}(pointer, $0) }
}
{% for cons in obj.alternate_constructors() %}
-
- public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ type_name }} {
- return {{ type_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
- }
-
+ {%- call swift::func_decl("public static func", cons, 4) %}
{% endfor %}
- {# // 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_async() %}
-
- public func {{ meth.name()|fn_name }}({%- call swift::arg_list_decl(meth) -%}) async {% call swift::throws(meth) %}{% match meth.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
- return {% call swift::try(meth) %} await uniffiRustCallAsync(
- rustFutureFunc: {
- {{ meth.ffi_func().name() }}(
- self.pointer
- {%- for arg in meth.arguments() -%}
- ,
- {{ arg|lower_fn }}({{ arg.name()|var_name }})
- {%- endfor %}
- )
- },
- pollFunc: {{ meth.ffi_rust_future_poll(ci) }},
- completeFunc: {{ meth.ffi_rust_future_complete(ci) }},
- freeFunc: {{ meth.ffi_rust_future_free(ci) }},
- {%- match meth.return_type() %}
- {%- when Some(return_type) %}
- liftFunc: {{ return_type|lift_fn }},
- {%- when None %}
- liftFunc: { $0 },
- {%- endmatch %}
- {%- match meth.throws_type() %}
- {%- when Some with (e) %}
- errorHandler: {{ e|ffi_converter_name }}.lift
- {%- else %}
- errorHandler: nil
- {% endmatch %}
+ {%- call swift::func_decl("open func", meth, 4) %}
+ {% endfor %}
+
+ {%- for tm in obj.uniffi_traits() %}
+ {%- match tm %}
+ {%- when UniffiTrait::Display { fmt } %}
+ open var description: String {
+ return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(fmt) %}
)
}
-
- {% else -%}
-
- {%- match meth.return_type() -%}
-
- {%- when Some with (return_type) %}
-
- 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.pointer", meth) %}
+ {%- when UniffiTrait::Debug { fmt } %}
+ open var debugDescription: String {
+ return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(fmt) %}
)
}
-
- {%- 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.pointer", meth) %}
+ {%- when UniffiTrait::Eq { eq, ne } %}
+ public static func == (self: {{ impl_class_name }}, other: {{ impl_class_name }}) -> Bool {
+ return {% call swift::try(eq) %} {{ eq.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(eq) %}
+ )
+ }
+ {%- when UniffiTrait::Hash { hash } %}
+ open func hash(into hasher: inout Hasher) {
+ let val = {% call swift::try(hash) %} {{ hash.return_type().unwrap()|lift_fn }}(
+ {% call swift::to_ffi_call(hash) %}
+ )
+ hasher.combine(val)
}
+ {%- else %}
+ {%- endmatch %}
+ {%- endfor %}
- {%- endmatch -%}
- {%- endif -%}
- {% endfor %}
}
+{%- if obj.has_callback_interface() %}
+{%- let callback_handler = format!("uniffiCallbackInterface{}", name) %}
+{%- let callback_init = format!("uniffiCallbackInit{}", name) %}
+{%- let vtable = obj.vtable().expect("trait interface should have a vtable") %}
+{%- let vtable_methods = obj.vtable_methods() %}
+{%- let ffi_init_callback = obj.ffi_init_callback() %}
+{% include "CallbackInterfaceImpl.swift" %}
+{%- endif %}
+
public struct {{ ffi_converter_name }}: FfiConverter {
+ {%- if obj.has_callback_interface() %}
+ fileprivate static var handleMap = UniffiHandleMap<{{ type_name }}>()
+ {%- endif %}
+
typealias FfiType = UnsafeMutableRawPointer
typealias SwiftType = {{ type_name }}
+ public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
+ return {{ impl_class_name }}(unsafeFromRawPointer: pointer)
+ }
+
+ public static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
+ {%- if obj.has_callback_interface() %}
+ guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else {
+ fatalError("Cast to UnsafeMutableRawPointer failed")
+ }
+ return ptr
+ {%- else %}
+ return value.uniffiClonePointer()
+ {%- endif %}
+ }
+
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
let v: UInt64 = try readInt(&buf)
// The Rust code won't compile if a pointer won't fit in a UInt64.
@@ -115,15 +157,30 @@ public struct {{ ffi_converter_name }}: FfiConverter {
// The Rust code won't compile if a pointer won't fit in a `UInt64`.
writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
}
+}
- public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
- return {{ type_name}}(unsafeFromRawPointer: pointer)
+{# Objects as error #}
+{%- if is_error %}
+{# Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer holding a pointer #}
+public struct {{ ffi_converter_name }}__as_error: FfiConverterRustBuffer {
+ public static func lift(_ buf: RustBuffer) throws -> {{ type_name }} {
+ var reader = createReader(data: Data(rustBuffer: buf))
+ return try {{ ffi_converter_name }}.read(from: &reader)
}
- public static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
- return value.pointer
+ public static func lower(_ value: {{ type_name }}) -> RustBuffer {
+ fatalError("not implemented")
+ }
+
+ public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
+ fatalError("not implemented")
+ }
+
+ public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
+ fatalError("not implemented")
}
}
+{%- endif %}
{#
We always write these public functions just in case the enum is used as
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift
new file mode 100644
index 0000000000..7df953558a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift
@@ -0,0 +1,12 @@
+{%- call swift::docstring_value(protocol_docstring, 0) %}
+public protocol {{ protocol_name }} : AnyObject {
+ {% for meth in methods.iter() -%}
+ {%- call swift::docstring(meth, 4) %}
+ func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) -%}{% call swift::throws(meth) -%}
+ {%- match meth.return_type() -%}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name -}}
+ {%- else -%}
+ {%- endmatch %}
+ {% endfor %}
+}
+
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
index 44de9dd358..c262a7a216 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift
@@ -1,12 +1,14 @@
{%- let rec = ci|get_record_definition(name) %}
+{%- call swift::docstring(rec, 0) %}
public struct {{ type_name }} {
{%- for field in rec.fields() %}
- public var {{ field.name()|var_name }}: {{ field|type_name }}
+ {%- call swift::docstring(field, 4) %}
+ public {% if config.generate_immutable_records() %}let{% else %}var{% endif %} {{ field.name()|var_name }}: {{ field|type_name }}
{%- endfor %}
// Default memberwise initializers are never public by default, so we
// declare one manually.
- public init({% call swift::field_list_decl(rec) %}) {
+ public init({% call swift::field_list_decl(rec, false) %}) {
{%- for field in rec.fields() %}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
@@ -14,6 +16,7 @@ public struct {{ type_name }} {
}
{% if !contains_object_references %}
+{% if config.experimental_sendable_value_types() %}extension {{ type_name }}: Sendable {} {% endif %}
extension {{ type_name }}: Equatable, Hashable {
public static func ==(lhs: {{ type_name }}, rhs: {{ type_name }}) -> Bool {
{%- for field in rec.fields() %}
@@ -34,12 +37,16 @@ extension {{ type_name }}: Equatable, Hashable {
public struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> {{ type_name }} {
- return try {{ type_name }}(
+ return {%- if rec.has_fields() %}
+ try {{ type_name }}(
{%- for field in rec.fields() %}
- {{ field.name()|arg_name }}: {{ field|read_fn }}(from: &buf)
- {%- if !loop.last %}, {% endif %}
+ {{ field.name()|arg_name }}: {{ field|read_fn }}(from: &buf)
+ {%- if !loop.last %}, {% endif %}
{%- endfor %}
)
+ {%- else %}
+ {{ type_name }}()
+ {%- endif %}
}
public static func write(_ value: {{ type_name }}, into buf: inout [UInt8]) {
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
index 2f737b6635..a053334a30 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift
@@ -7,6 +7,10 @@ fileprivate extension RustBuffer {
self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
}
+ static func empty() -> RustBuffer {
+ RustBuffer(capacity: 0, len:0, data: nil)
+ }
+
static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
try! rustCall { {{ ci.ffi_rustbuffer_from_bytes().name() }}(ForeignBytes(bufferPointer: ptr), $0) }
}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
index a2c6311931..ce946076f7 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift
@@ -1,48 +1 @@
-{%- if func.is_async() %}
-
-public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) async {% call swift::throws(func) %}{% match func.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_name }}{% when None %}{% endmatch %} {
- return {% call swift::try(func) %} await uniffiRustCallAsync(
- rustFutureFunc: {
- {{ func.ffi_func().name() }}(
- {%- for arg in func.arguments() %}
- {{ arg|lower_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
- {%- endfor %}
- )
- },
- pollFunc: {{ func.ffi_rust_future_poll(ci) }},
- completeFunc: {{ func.ffi_rust_future_complete(ci) }},
- freeFunc: {{ func.ffi_rust_future_free(ci) }},
- {%- match func.return_type() %}
- {%- when Some(return_type) %}
- liftFunc: {{ return_type|lift_fn }},
- {%- when None %}
- liftFunc: { $0 },
- {%- endmatch %}
- {%- match func.throws_type() %}
- {%- when Some with (e) %}
- errorHandler: {{ e|ffi_converter_name }}.lift
- {%- else %}
- errorHandler: nil
- {% endmatch %}
- )
-}
-
-{% else %}
-
-{%- match func.return_type() -%}
-{%- when Some with (return_type) %}
-
-public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {% call swift::throws(func) %} -> {{ return_type|type_name }} {
- return {% call swift::try(func) %} {{ return_type|lift_fn }}(
- {% call swift::to_ffi_call(func) %}
- )
-}
-
-{%- when None %}
-
-public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} {
- {% call swift::to_ffi_call(func) %}
-}
-
-{% endmatch %}
-{%- endif %}
+{%- call swift::func_decl("public func", func, 0) %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
index aba34f4b0b..5e26758f3c 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/Types.swift
@@ -64,9 +64,6 @@
{%- when Type::CallbackInterface { name, module_path } %}
{%- include "CallbackInterfaceTemplate.swift" %}
-{%- when Type::ForeignExecutor %}
-{%- include "ForeignExecutorTemplate.swift" %}
-
{%- when Type::Custom { name, module_path, builtin } %}
{%- include "CustomType.swift" %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
index 0a125e6f61..8692cd6ff0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/macros.swift
@@ -8,28 +8,97 @@
{%- call try(func) -%}
{%- match func.throws_type() -%}
{%- when Some with (e) -%}
- rustCallWithError({{ e|ffi_converter_name }}.lift) {
+ rustCallWithError({{ e|ffi_error_converter_name }}.lift) {
{%- else -%}
rustCall() {
{%- endmatch %}
- {{ func.ffi_func().name() }}({% call arg_list_lowered(func) -%} $0)
+ {{ func.ffi_func().name() }}(
+ {%- if func.takes_self() %}self.uniffiClonePointer(),{% endif %}
+ {%- call arg_list_lowered(func) -%} $0
+ )
}
{%- endmacro -%}
-{%- macro to_ffi_call_with_prefix(prefix, func) -%}
-{% call try(func) %}
- {%- match func.throws_type() %}
- {%- when Some with (e) %}
- rustCallWithError({{ e|ffi_converter_name }}.lift) {
+// eg, `public func foo_bar() { body }`
+{%- macro func_decl(func_decl, callable, indent) %}
+{%- call docstring(callable, indent) %}
+{{ func_decl }} {{ callable.name()|fn_name }}(
+ {%- call arg_list_decl(callable) -%})
+ {%- call async(callable) %}
+ {%- call throws(callable) %}
+ {%- match callable.return_type() %}
+ {%- when Some with (return_type) %} -> {{ return_type|type_name }}
+ {%- when None %}
+ {%- endmatch %} {
+ {%- call call_body(callable) %}
+}
+{%- endmacro %}
+
+// primary ctor - no name, no return-type.
+{%- macro ctor_decl(callable, indent) %}
+{%- call docstring(callable, indent) %}
+public convenience init(
+ {%- call arg_list_decl(callable) -%}) {%- call async(callable) %} {%- call throws(callable) %} {
+ {%- if callable.is_async() %}
+ let pointer =
+ {%- call call_async(callable) %}
+ {# The async mechanism returns an already constructed self.
+ We work around that by cloning the pointer from that object, then
+ assune the old object dies as there are no other references possible.
+ #}
+ .uniffiClonePointer()
{%- else %}
- rustCall() {
- {% endmatch %}
- {{ func.ffi_func().name() }}(
- {{- prefix }}, {% call arg_list_lowered(func) -%} $0
- )
+ let pointer =
+ {% call to_ffi_call(callable) %}
+ {%- endif %}
+ self.init(unsafeFromRawPointer: pointer)
}
{%- endmacro %}
+{%- macro call_body(callable) %}
+{%- if callable.is_async() %}
+ return {%- call call_async(callable) %}
+{%- else %}
+{%- match callable.return_type() -%}
+{%- when Some with (return_type) %}
+ return {% call try(callable) %} {{ return_type|lift_fn }}({% call to_ffi_call(callable) %})
+{%- when None %}
+{%- call to_ffi_call(callable) %}
+{%- endmatch %}
+{%- endif %}
+
+{%- endmacro %}
+
+{%- macro call_async(callable) %}
+ {% call try(callable) %} await uniffiRustCallAsync(
+ rustFutureFunc: {
+ {{ callable.ffi_func().name() }}(
+ {%- if callable.takes_self() %}
+ self.uniffiClonePointer(){% if !callable.arguments().is_empty() %},{% endif %}
+ {% endif %}
+ {%- for arg in callable.arguments() -%}
+ {{ arg|lower_fn }}({{ arg.name()|var_name }}){% if !loop.last %},{% endif %}
+ {%- endfor %}
+ )
+ },
+ pollFunc: {{ callable.ffi_rust_future_poll(ci) }},
+ completeFunc: {{ callable.ffi_rust_future_complete(ci) }},
+ freeFunc: {{ callable.ffi_rust_future_free(ci) }},
+ {%- match callable.return_type() %}
+ {%- when Some(return_type) %}
+ liftFunc: {{ return_type|lift_fn }},
+ {%- when None %}
+ liftFunc: { $0 },
+ {%- endmatch %}
+ {%- match callable.throws_type() %}
+ {%- when Some with (e) %}
+ errorHandler: {{ e|ffi_error_converter_name }}.lift
+ {%- else %}
+ errorHandler: nil
+ {% endmatch %}
+ )
+{%- endmacro %}
+
{%- macro arg_list_lowered(func) %}
{%- for arg in func.arguments() %}
{{ arg|lower_fn }}({{ arg.name()|var_name }}),
@@ -56,17 +125,30 @@
// Field lists as used in Swift declarations of Records and Enums.
// Note the var_name and type_name filters.
-#}
-{% macro field_list_decl(item) %}
+{% macro field_list_decl(item, has_nameless_fields) %}
{%- for field in item.fields() -%}
+ {%- call docstring(field, 8) %}
+ {%- if has_nameless_fields %}
+ {{- field|type_name -}}
+ {%- if !loop.last -%}, {%- endif -%}
+ {%- else -%}
{{ field.name()|var_name }}: {{ field|type_name -}}
{%- match field.default_value() %}
{%- when Some with(literal) %} = {{ literal|literal_swift(field) }}
{%- else %}
{%- endmatch -%}
{% if !loop.last %}, {% endif %}
+ {%- endif -%}
{%- endfor %}
{%- endmacro %}
+{% macro field_name(field, field_num) %}
+{%- if field.name().is_empty() -%}
+v{{- field_num -}}
+{%- else -%}
+{{ field.name()|var_name }}
+{%- endif -%}
+{%- endmacro %}
{% macro arg_list_protocol(func) %}
{%- for arg in func.arguments() -%}
@@ -75,15 +157,26 @@
{%- endfor %}
{%- endmacro %}
-
{%- macro async(func) %}
-{%- if func.is_async() %}async{% endif %}
+{%- if func.is_async() %}async {% endif %}
{%- endmacro -%}
{%- macro throws(func) %}
-{%- if func.throws() %}throws{% endif %}
+{%- if func.throws() %}throws {% endif %}
{%- endmacro -%}
{%- macro try(func) %}
{%- if func.throws() %}try {% else %}try! {% endif %}
{%- endmacro -%}
+
+{%- macro docstring_value(maybe_docstring, indent_spaces) %}
+{%- match maybe_docstring %}
+{%- when Some(docstring) %}
+{{ docstring|docstring(indent_spaces) }}
+{%- else %}
+{%- endmatch %}
+{%- endmacro %}
+
+{%- macro docstring(defn, indent_spaces) %}
+{%- call docstring_value(defn.docstring(), indent_spaces) %}
+{%- endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
index c34d348efb..17fdde74e0 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift
@@ -1,5 +1,10 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
+
+// swiftlint:disable all
+
+{%- call swift::docstring_value(ci.namespace_docstring(), 0) %}
+
{%- import "macros.swift" as swift %}
import Foundation
{%- for imported_class in self.imports() %}
@@ -15,6 +20,7 @@ import {{ config.ffi_module_name() }}
{% include "RustBufferTemplate.swift" %}
{% include "Helpers.swift" %}
+{% include "HandleMap.swift" %}
// Public interface members begin here.
{{ type_helper_code }}
@@ -66,3 +72,5 @@ private func uniffiEnsureInitialized() {
fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
}
+
+// swiftlint:enable all
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
index c3b2f15277..195a77696b 100644
--- a/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
+++ b/third_party/rust/uniffi_bindgen/src/bindings/swift/test.rs
@@ -2,10 +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 crate::{
- bindings::{RunScriptOptions, TargetLanguage},
- library_mode::generate_bindings,
-};
+use crate::{bindings::RunScriptOptions, library_mode::generate_bindings, BindingGeneratorDefault};
use anyhow::{bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
@@ -15,6 +12,8 @@ use std::io::Write;
use std::process::{Command, Stdio};
use uniffi_testing::UniFFITestHelper;
+use crate::bindings::TargetLanguage;
+
/// Run Swift tests for a UniFFI test fixture
pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
run_script(
@@ -36,7 +35,7 @@ pub fn run_script(
args: Vec<String>,
options: &RunScriptOptions,
) -> Result<()> {
- let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
+ let script_path = Utf8Path::new(script_file).canonicalize_utf8()?;
let test_helper = UniFFITestHelper::new(crate_name)?;
let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
@@ -126,8 +125,17 @@ struct GeneratedSources {
impl GeneratedSources {
fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result<Self> {
- let sources =
- generate_bindings(cdylib_path, None, &[TargetLanguage::Swift], out_dir, false)?;
+ let sources = generate_bindings(
+ cdylib_path,
+ None,
+ &BindingGeneratorDefault {
+ target_languages: vec![TargetLanguage::Swift],
+ try_format_code: false,
+ },
+ None,
+ out_dir,
+ false,
+ )?;
let main_source = sources
.iter()
.find(|s| s.package.name == crate_name)
@@ -169,7 +177,7 @@ fn create_command(program: &str, options: &RunScriptOptions) -> Command {
if !options.show_compiler_messages {
// This prevents most compiler messages, but not remarks
command.arg("-suppress-warnings");
- // This gets the remarks. Note: swift will eventually get a `-supress-remarks` argument,
+ // This gets the remarks. Note: swift will eventually get a `-suppress-remarks` argument,
// maybe we can eventually move to that
command.stderr(Stdio::null());
}