summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/scaffolding
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/uniffi_bindgen/src/scaffolding
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs152
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs197
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs45
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs95
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs48
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs73
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs33
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/RustBuffer.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs17
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs118
-rw-r--r--third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs58
12 files changed, 890 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
new file mode 100644
index 0000000000..7fc60dede4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/mod.rs
@@ -0,0 +1,152 @@
+/* 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 anyhow::Result;
+use askama::Template;
+use std::borrow::Borrow;
+
+use super::interface::*;
+use heck::ToSnakeCase;
+
+#[derive(Template)]
+#[template(syntax = "rs", escape = "none", path = "scaffolding_template.rs")]
+pub struct RustScaffolding<'a> {
+ ci: &'a ComponentInterface,
+ uniffi_version: &'static str,
+}
+impl<'a> RustScaffolding<'a> {
+ pub fn new(ci: &'a ComponentInterface) -> Self {
+ Self {
+ ci,
+ uniffi_version: crate::BINDGEN_VERSION,
+ }
+ }
+}
+mod filters {
+ use super::*;
+
+ pub fn type_rs(type_: &Type) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ Type::Int8 => "i8".into(),
+ Type::UInt8 => "u8".into(),
+ Type::Int16 => "i16".into(),
+ Type::UInt16 => "u16".into(),
+ Type::Int32 => "i32".into(),
+ Type::UInt32 => "u32".into(),
+ Type::Int64 => "i64".into(),
+ Type::UInt64 => "u64".into(),
+ Type::Float32 => "f32".into(),
+ Type::Float64 => "f64".into(),
+ Type::Boolean => "bool".into(),
+ Type::String => "String".into(),
+ Type::Timestamp => "std::time::SystemTime".into(),
+ Type::Duration => "std::time::Duration".into(),
+ Type::Enum(name) | Type::Record(name) | Type::Error(name) => format!("r#{name}"),
+ Type::Object(name) => format!("std::sync::Arc<r#{name}>"),
+ Type::CallbackInterface(name) => format!("Box<dyn r#{name}>"),
+ Type::Optional(t) => format!("std::option::Option<{}>", type_rs(t)?),
+ Type::Sequence(t) => format!("std::vec::Vec<{}>", type_rs(t)?),
+ Type::Map(k, v) => format!(
+ "std::collections::HashMap<{}, {}>",
+ type_rs(k)?,
+ type_rs(v)?
+ ),
+ Type::Custom { name, .. } => format!("r#{name}"),
+ Type::External { .. } => panic!("External types coming to a uniffi near you soon!"),
+ Type::Unresolved { .. } => {
+ unreachable!("UDL scaffolding code never contains unresolved types")
+ }
+ })
+ }
+
+ pub fn type_ffi(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(match type_ {
+ FfiType::Int8 => "i8".into(),
+ FfiType::UInt8 => "u8".into(),
+ FfiType::Int16 => "i16".into(),
+ FfiType::UInt16 => "u16".into(),
+ FfiType::Int32 => "i32".into(),
+ FfiType::UInt32 => "u32".into(),
+ FfiType::Int64 => "i64".into(),
+ FfiType::UInt64 => "u64".into(),
+ FfiType::Float32 => "f32".into(),
+ FfiType::Float64 => "f64".into(),
+ FfiType::RustArcPtr(_) => "*const std::os::raw::c_void".into(),
+ FfiType::RustBuffer(_) => "uniffi::RustBuffer".into(),
+ FfiType::ForeignBytes => "uniffi::ForeignBytes".into(),
+ FfiType::ForeignCallback => "uniffi::ForeignCallback".into(),
+ })
+ }
+
+ /// Get the name of the FfiConverter implementation for this type
+ ///
+ /// - For primitives / standard types this is the type itself.
+ /// - For user-defined types, this is a unique generated name. We then generate a unit-struct
+ /// in the scaffolding code that implements FfiConverter.
+ pub fn ffi_converter_name(type_: &Type) -> askama::Result<String> {
+ Ok(match type_ {
+ // Timestamp/Duraration are handled by standard types
+ Type::Timestamp => "std::time::SystemTime".into(),
+ Type::Duration => "std::time::Duration".into(),
+ // Object is handled by Arc<T>
+ Type::Object(name) => format!("std::sync::Arc<r#{name}>"),
+ // Other user-defined types are handled by a unit-struct that we generate. The
+ // FfiConverter implementation for this can be found in one of the scaffolding template code.
+ //
+ // We generate a unit-struct to sidestep Rust's orphan rules (ADR-0006).
+ //
+ // CallbackInterface is handled by special case code on both the scaffolding and
+ // bindings side. It's not a unit-struct, but the same name generation code works.
+ Type::Enum(_) | Type::Record(_) | Type::Error(_) | Type::CallbackInterface(_) => {
+ format!("FfiConverter{}", type_.canonical_name())
+ }
+ // Wrapper types are implemented by generics that wrap the FfiConverter implementation of the
+ // inner type.
+ Type::Optional(inner) => {
+ format!("std::option::Option<{}>", ffi_converter_name(inner)?)
+ }
+ Type::Sequence(inner) => format!("std::vec::Vec<{}>", ffi_converter_name(inner)?),
+ Type::Map(k, v) => format!(
+ "std::collections::HashMap<{}, {}>",
+ ffi_converter_name(k)?,
+ ffi_converter_name(v)?
+ ),
+ // External and Wrapped bytes have FfiConverters with a predictable name based on the type name.
+ Type::Custom { name, .. } | Type::External { name, .. } => {
+ format!("FfiConverterType{name}")
+ }
+ // Primitive types / strings are implemented by their rust type
+ Type::Int8 => "i8".into(),
+ Type::UInt8 => "u8".into(),
+ Type::Int16 => "i16".into(),
+ Type::UInt16 => "u16".into(),
+ Type::Int32 => "i32".into(),
+ Type::UInt32 => "u32".into(),
+ Type::Int64 => "i64".into(),
+ Type::UInt64 => "u64".into(),
+ Type::Float32 => "f32".into(),
+ Type::Float64 => "f64".into(),
+ Type::String => "String".into(),
+ Type::Boolean => "bool".into(),
+ Type::Unresolved { .. } => {
+ unreachable!("UDL scaffolding code never contains unresolved types")
+ }
+ })
+ }
+
+ // Map a type to Rust code that specifies the FfiConverter implementation.
+ //
+ // This outputs something like `<TheFfiConverterStruct as FfiConverter>`
+ pub fn ffi_converter(type_: &Type) -> Result<String, askama::Error> {
+ Ok(format!(
+ "<{} as uniffi::FfiConverter>",
+ ffi_converter_name(type_)?
+ ))
+ }
+
+ // Turns a `crate-name` into the `crate_name` the .rs code needs to specify.
+ pub fn crate_name_rs(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.to_string().to_snake_case())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
new file mode 100644
index 0000000000..58489fd6f6
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/CallbackInterfaceTemplate.rs
@@ -0,0 +1,197 @@
+{#
+// For each Callback Interface definition, we assume that there is a corresponding trait defined in Rust client code.
+// If the UDL callback interface and Rust trait's methods don't match, the Rust compiler will complain.
+// We generate:
+// * an init function to accept that `ForeignCallback` from the foreign language, and stores it.
+// * a holder for a `ForeignCallback`, of type `uniffi::ForeignCallbackInternals`.
+// * a proxy `struct` which implements the `trait` that the Callback Interface corresponds to. This
+// is the object that client code interacts with.
+// - for each method, arguments will be packed into a `RustBuffer` and sent over the `ForeignCallback` to be
+// unpacked and called. The return value is packed into another `RustBuffer` and sent back to Rust.
+// - a `Drop` `impl`, which tells the foreign language to forget about the real callback object.
+#}
+{% let trait_name = cbi.name() -%}
+{% let trait_impl = cbi.type_().borrow()|ffi_converter_name -%}
+{% let foreign_callback_internals = format!("foreign_callback_{}_internals", trait_name)|upper -%}
+
+// Register a foreign callback for getting across the FFI.
+#[doc(hidden)]
+static {{ foreign_callback_internals }}: uniffi::ForeignCallbackInternals = uniffi::ForeignCallbackInternals::new();
+
+#[doc(hidden)]
+#[no_mangle]
+pub extern "C" fn {{ cbi.ffi_init_callback().name() }}(callback: uniffi::ForeignCallback, _: &mut uniffi::RustCallStatus) {
+ {{ foreign_callback_internals }}.set_callback(callback);
+ // The call status should be initialized to CALL_SUCCESS, so no need to modify it.
+}
+
+// Make an implementation which will shell out to the foreign language.
+#[doc(hidden)]
+#[derive(Debug)]
+struct {{ trait_impl }} {
+ handle: u64
+}
+
+impl Drop for {{ trait_impl }} {
+ fn drop(&mut self) {
+ let callback = {{ foreign_callback_internals }}.get_callback().unwrap();
+ let mut rbuf = uniffi::RustBuffer::new();
+ unsafe { callback(self.handle, uniffi::IDX_CALLBACK_FREE, Default::default(), &mut rbuf) };
+ }
+}
+
+uniffi::deps::static_assertions::assert_impl_all!({{ trait_impl }}: Send);
+
+impl r#{{ trait_name }} for {{ trait_impl }} {
+ {%- for meth in cbi.methods() %}
+
+ {#- Method declaration #}
+ fn r#{{ meth.name() -}}
+ ({% call rs::arg_list_decl_with_prefix("&self", meth) %})
+ {%- match (meth.return_type(), meth.throws_type()) %}
+ {%- when (Some(return_type), None) %} -> {{ return_type.borrow()|type_rs }}
+ {%- when (Some(return_type), Some(err)) %} -> ::std::result::Result<{{ return_type.borrow()|type_rs }}, {{ err|type_rs }}>
+ {%- when (None, Some(err)) %} -> ::std::result::Result<(), {{ err|type_rs }}>
+ {% else -%}
+ {%- endmatch -%} {
+ {#- Method body #}
+
+ {#- Packing args into a RustBuffer #}
+ {% if meth.arguments().len() == 0 -%}
+ let args_buf = Vec::new();
+ {% else -%}
+ let mut args_buf = Vec::new();
+ {% endif -%}
+ {%- for arg in meth.arguments() %}
+ {{ arg.type_().borrow()|ffi_converter }}::write(r#{{ arg.name() }}, &mut args_buf);
+ {%- endfor -%}
+ let args_rbuf = uniffi::RustBuffer::from_vec(args_buf);
+
+ {#- Calling into foreign code. #}
+ let callback = {{ foreign_callback_internals }}.get_callback().unwrap();
+
+ unsafe {
+ // SAFETY:
+ // * We're passing in a pointer to an empty buffer.
+ // * Nothing allocated, so nothing to drop.
+ // * We expect the callback to write into that a valid allocated instance of a
+ // RustBuffer.
+ let mut ret_rbuf = uniffi::RustBuffer::new();
+ let ret = callback(self.handle, {{ loop.index }}, args_rbuf, &mut ret_rbuf);
+ #[allow(clippy::let_and_return, clippy::let_unit_value)]
+ match ret {
+ 1 => {
+ // 1 indicates success with the return value written to the RustBuffer for
+ // non-void calls.
+ let result = {
+ {% match meth.return_type() -%}
+ {%- when Some(return_type) -%}
+ let vec = ret_rbuf.destroy_into_vec();
+ let mut ret_buf = vec.as_slice();
+ {{ return_type|ffi_converter }}::try_read(&mut ret_buf).unwrap()
+ {%- else %}
+ uniffi::RustBuffer::destroy(ret_rbuf);
+ {%- endmatch %}
+ };
+ {%- if meth.throws() %}
+ Ok(result)
+ {%- else %}
+ result
+ {%- endif %}
+ }
+ -2 => {
+ // -2 indicates an error written to the RustBuffer
+ {% match meth.throws_type() -%}
+ {% when Some(error_type) -%}
+ let vec = ret_rbuf.destroy_into_vec();
+ let mut ret_buf = vec.as_slice();
+ Err({{ error_type|ffi_converter }}::try_read(&mut ret_buf).unwrap())
+ {%- else -%}
+ panic!("Callback return -2, but throws_type() is None");
+ {%- endmatch %}
+ }
+ // 0 is a deprecated method to indicates success for void returns
+ 0 => {
+ uniffi::deps::log::error!("UniFFI: Callback interface returned 0. Please update the bindings code to return 1 for all successful calls");
+ {% match (meth.return_type(), meth.throws()) %}
+ {% when (Some(_), _) %}
+ panic!("Callback returned 0 when we were expecting a return value");
+ {% when (None, false) %}
+ {% when (None, true) %}
+ Ok(())
+ {%- endmatch %}
+ }
+ // -1 indicates an unexpected error
+ {% match meth.throws_type() %}
+ {%- when Some(error_type) -%}
+ -1 => {
+ let reason = if !ret_rbuf.is_empty() {
+ match {{ Type::String.borrow()|ffi_converter }}::try_lift(ret_rbuf) {
+ Ok(s) => s,
+ Err(e) => {
+ uniffi::deps::log::error!("{{ trait_name }} Error reading ret_buf: {e}");
+ String::from("[Error reading reason]")
+ }
+ }
+ } else {
+ uniffi::RustBuffer::destroy(ret_rbuf);
+ String::from("[Unknown Reason]")
+ };
+ let e: {{ error_type|type_rs }} = uniffi::UnexpectedUniFFICallbackError::from_reason(reason).into();
+ Err(e)
+ }
+ {%- else %}
+ -1 => {
+ if !ret_rbuf.is_empty() {
+ let reason = match {{ Type::String.borrow()|ffi_converter }}::try_lift(ret_rbuf) {
+ Ok(s) => s,
+ Err(_) => {
+ String::from("[Error reading reason]")
+ }
+ };
+ panic!("callback failed. Reason: {}", reason);
+ } else {
+ panic!("Callback failed")
+ }
+ },
+ {%- endmatch %}
+ // Other values should never be returned
+ _ => panic!("Callback failed with unexpected return code"),
+ }
+ }
+ }
+ {%- endfor %}
+}
+
+unsafe impl uniffi::FfiConverter for {{ trait_impl }} {
+ // This RustType allows for rust code that inputs this type as a Box<dyn CallbackInterfaceTrait> param
+ type RustType = Box<dyn r#{{ trait_name }}>;
+ type FfiType = u64;
+
+ // Lower and write are tricky to implement because we have a dyn trait as our type. There's
+ // probably a way to, but this carries lots of thread safety risks, down to impedance
+ // mismatches between Rust and foreign languages, and our uncertainty around implementations of
+ // concurrent handlemaps.
+ //
+ // The use case for them is also quite exotic: it's passing a foreign callback back to the foreign
+ // language.
+ //
+ // Until we have some certainty, and use cases, we shouldn't use them.
+ fn lower(_obj: Self::RustType) -> Self::FfiType {
+ panic!("Lowering CallbackInterface not supported")
+ }
+
+ fn write(_obj: Self::RustType, _buf: &mut std::vec::Vec<u8>) {
+ panic!("Writing CallbackInterface not supported")
+ }
+
+ fn try_lift(v: Self::FfiType) -> uniffi::deps::anyhow::Result<Self::RustType> {
+ Ok(Box::new(Self { handle: v }))
+ }
+
+ fn try_read(buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<Self::RustType> {
+ use uniffi::deps::bytes::Buf;
+ uniffi::check_remaining(buf, 8)?;
+ <Self as uniffi::FfiConverter>::try_lift(buf.get_u64())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
new file mode 100644
index 0000000000..0d83a99b90
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs
@@ -0,0 +1,45 @@
+{#
+// For each enum declared in the UDL, we assume the caller has provided a corresponding
+// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
+// compile if the provided struct has a different shape to the one declared in the UDL.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[doc(hidden)]
+pub struct {{ e.type_().borrow()|ffi_converter_name }};
+
+#[doc(hidden)]
+impl uniffi::RustBufferFfiConverter for {{ e.type_().borrow()|ffi_converter_name }} {
+ type RustType = r#{{ e.name() }};
+
+ fn write(obj: Self::RustType, buf: &mut std::vec::Vec<u8>) {
+ use uniffi::deps::bytes::BufMut;
+ match obj {
+ {%- for variant in e.variants() %}
+ r#{{ e.name() }}::r#{{ variant.name() }} { {% for field in variant.fields() %}r#{{ field.name() }}, {%- endfor %} } => {
+ buf.put_i32({{ loop.index }});
+ {% for field in variant.fields() -%}
+ {{ field.type_().borrow()|ffi_converter }}::write(r#{{ field.name() }}, buf);
+ {%- endfor %}
+ },
+ {%- endfor %}
+ };
+ }
+
+ fn try_read(buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<r#{{ e.name() }}> {
+ use uniffi::deps::bytes::Buf;
+ uniffi::check_remaining(buf, 4)?;
+ Ok(match buf.get_i32() {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} => r#{{ e.name() }}::r#{{ variant.name() }}{% if variant.has_fields() %} {
+ {% for field in variant.fields() %}
+ r#{{ field.name() }}: {{ field.type_().borrow()|ffi_converter }}::try_read(buf)?,
+ {%- endfor %}
+ }{% endif %},
+ {%- endfor %}
+ v => uniffi::deps::anyhow::bail!("Invalid {{ e.name() }} enum value: {}", v),
+ })
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
new file mode 100644
index 0000000000..7ca1d104fe
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ErrorTemplate.rs
@@ -0,0 +1,95 @@
+{#
+// For each error declared in the UDL, we assume the caller has provided a corresponding
+// rust `enum`. We provide the traits for sending it across the FFI, which will fail to
+// compile if the provided struct has a different shape to the one declared in the UDL.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[doc(hidden)]
+pub struct {{ e.type_().borrow()|ffi_converter_name }};
+
+#[doc(hidden)]
+impl uniffi::RustBufferFfiConverter for {{ e.type_().borrow()|ffi_converter_name }} {
+ type RustType = r#{{ e.name() }};
+
+ {% if e.is_flat() %}
+
+ // For "flat" error enums, we stringify the error on the Rust side and surface that
+ // as the error message in the foreign language.
+
+
+ fn write(obj: r#{{ e.name() }}, buf: &mut std::vec::Vec<u8>) {
+ use uniffi::deps::bytes::BufMut;
+ let msg = obj.to_string();
+ match obj {
+ {%- for variant in e.variants() %}
+ r#{{ e.name() }}::r#{{ variant.name() }}{..} => {
+ buf.put_i32({{ loop.index }});
+ <String as uniffi::FfiConverter>::write(msg, buf);
+ },
+ {%- endfor %}
+ };
+ }
+
+ {%- if ci.should_generate_error_read(e) %}
+ fn try_read(buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<r#{{ e.name() }}> {
+ use uniffi::deps::bytes::Buf;
+ uniffi::check_remaining(buf, 4)?;
+ Ok(match buf.get_i32() {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} => r#{{ e.name() }}::r#{{ variant.name() }}{ },
+ {%- endfor %}
+ v => uniffi::deps::anyhow::bail!("Invalid {{ e.name() }} enum value: {}", v),
+ })
+ }
+ {%- else %}
+ fn try_read(_buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<r#{{ e.name() }}> {
+ panic!("try_read not supported for flat errors");
+ }
+ {%- endif %}
+
+ {% else %}
+
+ // For rich structured enums, we map individual fields on the Rust side over to
+ // corresponding fields on the foreign-language side.
+ //
+ // If a variant doesn't have fields defined in the UDL, it's currently still possible that
+ // the Rust enum has fields and they're just not listed. In that case we use the `Variant{..}`
+ // syntax to match the variant while ignoring its fields.
+
+ fn write(obj: r#{{ e.name() }}, buf: &mut std::vec::Vec<u8>) {
+ use uniffi::deps::bytes::BufMut;
+ match obj {
+ {%- for variant in e.variants() %}
+ r#{{ e.name() }}::r#{{ variant.name() }}{% if variant.has_fields() %} { {% for field in variant.fields() %}r#{{ field.name() }}, {%- endfor %} }{% else %}{..}{% endif %} => {
+ buf.put_i32({{ loop.index }});
+ {% for field in variant.fields() -%}
+ {{ field.type_().borrow()|ffi_converter }}::write(r#{{ field.name() }}, buf);
+ {%- endfor %}
+ },
+ {%- endfor %}
+ };
+ }
+
+ fn try_read(buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<r#{{ e.name() }}> {
+ // Note: no need to call should_generate_error_read here, since it is always true for
+ // non-flat errors
+ use uniffi::deps::bytes::Buf;
+ uniffi::check_remaining(buf, 4)?;
+ Ok(match buf.get_i32() {
+ {%- for variant in e.variants() %}
+ {{ loop.index }} => r#{{ e.name() }}::r#{{ variant.name() }}{% if variant.has_fields() %} {
+ {% for field in variant.fields() %}
+ r#{{ field.name() }}: {{ field.type_().borrow()|ffi_converter }}::try_read(buf)?,
+ {%- endfor %}
+ }{% endif %},
+ {%- endfor %}
+ v => uniffi::deps::anyhow::bail!("Invalid {{ e.name() }} enum value: {}", v),
+ })
+ }
+ {% endif %}
+}
+
+impl uniffi::FfiError for {{ e.type_().borrow()|ffi_converter_name }} { }
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
new file mode 100644
index 0000000000..cfbe080190
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ExternalTypesTemplate.rs
@@ -0,0 +1,48 @@
+// Support for external types.
+
+// Types with an external `FfiConverter`...
+{% for (name, crate_name) in ci.iter_external_types() %}
+// `{{ name }}` is defined in `{{ crate_name }}`
+use {{ crate_name|crate_name_rs }}::FfiConverterType{{ name }};
+{% endfor %}
+
+// For custom scaffolding types we need to generate an FfiConverterType based on the
+// UniffiCustomTypeConverter implementation that the library supplies
+{% for (name, builtin) in ci.iter_custom_types() %}
+{% if loop.first %}
+
+// A trait that's in our crate for our external wrapped types to implement.
+trait UniffiCustomTypeConverter {
+ type Builtin;
+
+ fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> where Self: Sized;
+ fn from_custom(obj: Self) -> Self::Builtin;
+}
+
+{%- endif -%}
+
+// Type `{{ name }}` wraps a `{{ builtin.canonical_name() }}`
+#[doc(hidden)]
+pub struct FfiConverterType{{ name }};
+
+unsafe impl uniffi::FfiConverter for FfiConverterType{{ name }} {
+ type RustType = r#{{ name }};
+ type FfiType = {{ FfiType::from(builtin).borrow()|type_ffi }};
+
+ fn lower(obj: {{ name }} ) -> Self::FfiType {
+ <{{ builtin|type_rs }} as uniffi::FfiConverter>::lower(<{{ name }} as UniffiCustomTypeConverter>::from_custom(obj))
+ }
+
+ fn try_lift(v: Self::FfiType) -> uniffi::Result<{{ name }}> {
+ <r#{{ name }} as UniffiCustomTypeConverter>::into_custom(<{{ builtin|type_rs }} as uniffi::FfiConverter>::try_lift(v)?)
+ }
+
+ fn write(obj: {{ name }}, buf: &mut Vec<u8>) {
+ <{{ builtin|type_rs }} as uniffi::FfiConverter>::write(<{{ name }} as UniffiCustomTypeConverter>::from_custom(obj), buf);
+ }
+
+ fn try_read(buf: &mut &[u8]) -> uniffi::Result<r#{{ name }}> {
+ <{{ name }} as UniffiCustomTypeConverter>::into_custom(<{{ builtin|type_rs }} as uniffi::FfiConverter>::try_read(buf)?)
+ }
+}
+{%- endfor -%}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
new file mode 100644
index 0000000000..3834ad994e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
@@ -0,0 +1,73 @@
+// For each Object definition, we assume the caller has provided an appropriately-shaped `struct T`
+// with an `impl` for each method on the object. We create an `Arc<T>` for "safely" handing out
+// references to these structs to foreign language code, and we provide a `pub extern "C"` function
+// corresponding to each method.
+//
+// (Note that "safely" is in "scare quotes" - that's because we use functions on an `Arc` that
+// that are inherently unsafe, but the code we generate is safe in practice.)
+//
+// If the caller's implementation of the struct does not match with the methods or types specified
+// in the UDL, then the rust compiler will complain with a (hopefully at least somewhat helpful!)
+// error message when processing this generated code.
+
+{% if obj.uses_deprecated_threadsafe_attribute() %}
+// We want to mark this as `deprecated` - long story short, the only way to
+// sanely do this using `#[deprecated(..)]` is to generate a function with that
+// attribute, then generate call to that function in the object constructors.
+#[deprecated(
+ since = "0.11.0",
+ note = "The `[Threadsafe]` attribute on interfaces is now the default and its use is deprecated - you should upgrade \
+ `{{ obj.name() }}` to remove the `[ThreadSafe]` attribute. \
+ See https://github.com/mozilla/uniffi-rs/#thread-safety for more details"
+)]
+#[allow(non_snake_case)]
+fn uniffi_note_threadsafe_deprecation_{{ obj.name() }}() {}
+{% endif %}
+
+
+// All Object structs must be `Sync + Send`. The generated scaffolding will fail to compile
+// if they are not, but unfortunately it fails with an unactionably obscure error message.
+// By asserting the requirement explicitly, we help Rust produce a more scrutable error message
+// and thus help the user debug why the requirement isn't being met.
+uniffi::deps::static_assertions::assert_impl_all!(r#{{ obj.name() }}: Sync, Send);
+
+{% let ffi_free = obj.ffi_object_free() -%}
+#[doc(hidden)]
+#[no_mangle]
+pub extern "C" fn {{ ffi_free.name() }}(ptr: *const std::os::raw::c_void, call_status: &mut uniffi::RustCallStatus) {
+ uniffi::call_with_output(call_status, || {
+ assert!(!ptr.is_null());
+ {#- turn it into an Arc and explicitly drop it. #}
+ drop(unsafe { std::sync::Arc::from_raw(ptr as *const r#{{ obj.name() }}) })
+ })
+}
+
+{%- for cons in obj.constructors() %}
+ #[doc(hidden)]
+ #[no_mangle]
+ pub extern "C" fn r#{{ cons.ffi_func().name() }}(
+ {%- call rs::arg_list_ffi_decl(cons.ffi_func()) %}) -> *const std::os::raw::c_void /* *const {{ obj.name() }} */ {
+ uniffi::deps::log::debug!("{{ cons.ffi_func().name() }}");
+ {% if obj.uses_deprecated_threadsafe_attribute() %}
+ uniffi_note_threadsafe_deprecation_{{ obj.name() }}();
+ {% endif %}
+
+ // If the constructor 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.
+ {% call rs::to_rs_constructor_call(obj, cons) %}
+ }
+{%- endfor %}
+
+{%- for meth in obj.methods() %}
+ #[doc(hidden)]
+ #[no_mangle]
+ #[allow(clippy::let_unit_value)] // Sometimes we generate code that binds `_retval` to `()`.
+ pub extern "C" fn r#{{ meth.ffi_func().name() }}(
+ {%- call rs::arg_list_ffi_decl(meth.ffi_func()) %}
+ ) {% call rs::return_signature(meth) %} {
+ 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.
+ {% call rs::to_rs_method_call(obj, meth) %}
+ }
+{% endfor %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
new file mode 100644
index 0000000000..6ec85ed036
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RecordTemplate.rs
@@ -0,0 +1,33 @@
+{#
+// For each record declared in the UDL, we assume the caller has provided a corresponding
+// rust `struct` with the declared fields. We provide the traits for sending it across the FFI.
+// If the caller's struct does not match the shape and types declared in the UDL then the rust
+// compiler will complain with a type error.
+//
+// We define a unit-struct to implement the trait to sidestep Rust's orphan rule (ADR-0006). It's
+// public so other crates can refer to it via an `[External='crate'] typedef`
+#}
+
+#[doc(hidden)]
+pub struct {{ rec.type_().borrow()|ffi_converter_name }};
+
+#[doc(hidden)]
+impl uniffi::RustBufferFfiConverter for {{ rec.type_().borrow()|ffi_converter_name }} {
+ type RustType = r#{{ rec.name() }};
+
+ fn write(obj: r#{{ rec.name() }}, buf: &mut std::vec::Vec<u8>) {
+ // If the provided struct doesn't match the fields declared in the UDL, then
+ // the generated code here will fail to compile with somewhat helpful error.
+ {%- for field in rec.fields() %}
+ {{ field.type_().borrow()|ffi_converter }}::write(obj.r#{{ field.name() }}, buf);
+ {%- endfor %}
+ }
+
+ fn try_read(buf: &mut &[u8]) -> uniffi::deps::anyhow::Result<r#{{ rec.name() }}> {
+ Ok(r#{{ rec.name() }} {
+ {%- for field in rec.fields() %}
+ r#{{ field.name() }}: {{ field.type_().borrow()|ffi_converter }}::try_read(buf)?,
+ {%- endfor %}
+ })
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs
new file mode 100644
index 0000000000..668f1a2e63
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/ReexportUniFFIScaffolding.rs
@@ -0,0 +1,27 @@
+// Code to re-export the UniFFI scaffolding functions.
+//
+// Rust won't always re-export the functions from dependencies
+// ([rust-lang#50007](https://github.com/rust-lang/rust/issues/50007))
+//
+// A workaround for this is to have the dependent crate reference a function from its dependency in
+// an extern "C" function. This is clearly hacky and brittle, but at least we have some unittests
+// that check if this works (fixtures/reexport-scaffolding-macro).
+//
+// The main way we use this macro is for that contain multiple UniFFI components (libxul,
+// megazord). The combined library has a cargo dependency for each component and calls
+// uniffi_reexport_scaffolding!() for each one.
+
+#[doc(hidden)]
+pub fn uniffi_reexport_hack() {
+}
+
+#[macro_export]
+macro_rules! uniffi_reexport_scaffolding {
+ () => {
+ #[doc(hidden)]
+ #[no_mangle]
+ pub extern "C" fn {{ ci.namespace() }}_uniffi_reexport_hack() {
+ $crate::uniffi_reexport_hack()
+ }
+ };
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RustBuffer.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RustBuffer.rs
new file mode 100644
index 0000000000..39d3f32dcc
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/RustBuffer.rs
@@ -0,0 +1,27 @@
+// Everybody gets basic buffer support, since it's needed for passing complex types over the FFI.
+//
+// See `uniffi/src/ffi/rustbuffer.rs` for documentation on these functions
+
+#[allow(clippy::missing_safety_doc)]
+#[no_mangle]
+pub extern "C" fn {{ ci.ffi_rustbuffer_alloc().name() }}(size: i32, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
+ uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status)
+}
+
+#[allow(clippy::missing_safety_doc)]
+#[no_mangle]
+pub unsafe extern "C" fn {{ ci.ffi_rustbuffer_from_bytes().name() }}(bytes: uniffi::ForeignBytes, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
+ uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status)
+}
+
+#[allow(clippy::missing_safety_doc)]
+#[no_mangle]
+pub unsafe extern "C" fn {{ ci.ffi_rustbuffer_free().name() }}(buf: uniffi::RustBuffer, call_status: &mut uniffi::RustCallStatus) {
+ uniffi::ffi::uniffi_rustbuffer_free(buf, call_status)
+}
+
+#[allow(clippy::missing_safety_doc)]
+#[no_mangle]
+pub unsafe extern "C" fn {{ ci.ffi_rustbuffer_reserve().name() }}(buf: uniffi::RustBuffer, additional: i32, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
+ uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status)
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
new file mode 100644
index 0000000000..0c6d5a47de
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs
@@ -0,0 +1,17 @@
+{#
+// For each top-level function declared in the UDL, we assume the caller has provided a corresponding
+// rust function of the same name. We provide a `pub extern "C"` wrapper that does type conversions to
+// send data across the FFI, which will fail to compile if the provided function does not match what's
+// specified in the UDL.
+#}
+#[doc(hidden)]
+#[no_mangle]
+#[allow(clippy::let_unit_value)] // Sometimes we generate code that binds `_retval` to `()`.
+pub extern "C" fn r#{{ func.ffi_func().name() }}(
+ {% call rs::arg_list_ffi_decl(func.ffi_func()) %}
+) {% call rs::return_signature(func) %} {
+ // If the provided function does not match the signature specified in the UDL
+ // then this attempt to call it will not compile, and will give guidance as to why.
+ uniffi::deps::log::debug!("{{ func.ffi_func().name() }}");
+ {% call rs::to_rs_function_call(func) %}
+}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs
new file mode 100644
index 0000000000..7ee37c234e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/macros.rs
@@ -0,0 +1,118 @@
+{#
+// Template to receive calls into rust.
+#}
+
+{%- macro to_rs_call(func) -%}
+r#{{ func.name() }}({% call _arg_list_rs_call(func) -%})
+{%- endmacro -%}
+
+{%- macro _arg_list_rs_call(func) %}
+ {%- for arg in func.full_arguments() %}
+ match {{- arg.type_().borrow()|ffi_converter }}::try_lift(r#{{ arg.name() }}) {
+ {%- if arg.by_ref() %}
+ Ok(ref val) => val,
+ {%- else %}
+ Ok(val) => val,
+ {%- endif %}
+
+ {#- If this function returns an error, we attempt to downcast errors doing arg
+ conversions to this error. If the downcast fails or the function doesn't
+ return an error, we just panic.
+ -#}
+ {%- match func.throws_type() -%}
+ {%- when Some with (e) %}
+ Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::<{{ e|ffi_converter_name }}>(err, "{{ arg.name() }}")),
+ {%- else %}
+ Err(err) => panic!("Failed to convert arg '{}': {}", "{{ arg.name() }}", err),
+ {%- endmatch %}
+ }
+ {%- if !loop.last %},{% endif %}
+ {%- endfor %}
+{%- endmacro -%}
+
+{#-
+// Arglist as used in the _UniFFILib function declarations.
+// Note unfiltered name but type_ffi filters.
+-#}
+{%- macro arg_list_ffi_decl(func) %}
+ {%- for arg in func.arguments() %}
+ r#{{- arg.name() }}: {{ arg.type_().borrow()|type_ffi -}},
+ {%- endfor %}
+ call_status: &mut uniffi::RustCallStatus
+{%- endmacro -%}
+
+{%- macro arg_list_decl_with_prefix(prefix, meth) %}
+ {{- prefix -}}
+ {%- if meth.arguments().len() > 0 %}, {# whitespace #}
+ {%- for arg in meth.arguments() %}
+ r#{{- arg.name() }}: {{ arg.type_().borrow()|type_rs -}}{% if loop.last %}{% else %},{% endif %}
+ {%- endfor %}
+ {%- endif %}
+{%- endmacro -%}
+
+{% macro return_signature(func) %}{% match func.ffi_func().return_type() %}{% when Some with (return_type) %} -> {% call return_type_func(func) %}{%- else -%}{%- endmatch -%}{%- endmacro -%}
+
+{% macro return_type_func(func) %}{% match func.ffi_func().return_type() %}{% when Some with (return_type) %}{{ return_type|type_ffi }}{%- else -%}(){%- endmatch -%}{%- endmacro -%}
+
+{% macro ret(func) %}{% match func.return_type() %}{% when Some with (return_type) %}{{ return_type|ffi_converter }}::lower(_retval){% else %}_retval{% endmatch %}{% endmacro %}
+
+{% macro construct(obj, cons) %}
+ r#{{- obj.name() }}::{% call to_rs_call(cons) -%}
+{% endmacro %}
+
+{% macro to_rs_constructor_call(obj, cons) %}
+{% match cons.throws_type() %}
+{% when Some with (e) %}
+ uniffi::call_with_result(call_status, || {
+ let _new = {% call construct(obj, cons) %}.map_err(Into::into).map_err({{ e|ffi_converter }}::lower)?;
+ let _arc = std::sync::Arc::new(_new);
+ Ok({{ obj.type_().borrow()|ffi_converter }}::lower(_arc))
+ })
+{% else %}
+ uniffi::call_with_output(call_status, || {
+ let _new = {% call construct(obj, cons) %};
+ let _arc = std::sync::Arc::new(_new);
+ {{ obj.type_().borrow()|ffi_converter }}::lower(_arc)
+ })
+{% endmatch %}
+{% endmacro %}
+
+{% macro to_rs_method_call(obj, meth) -%}
+{% match meth.throws_type() -%}
+{% when Some with (e) -%}
+uniffi::call_with_result(call_status, || {
+ let _retval = r#{{ obj.name() }}::{% call to_rs_call(meth) %}.map_err(Into::into).map_err({{ e|ffi_converter }}::lower)?;
+ Ok({% call ret(meth) %})
+})
+{% else %}
+uniffi::call_with_output(call_status, || {
+ {% match meth.return_type() -%}
+ {% when Some with (return_type) -%}
+ let retval = r#{{ obj.name() }}::{% call to_rs_call(meth) %};
+ {{ return_type|ffi_converter }}::lower(retval)
+ {% else -%}
+ r#{{ obj.name() }}::{% call to_rs_call(meth) %}
+ {% endmatch -%}
+})
+{% endmatch -%}
+{% endmacro -%}
+
+{% macro to_rs_function_call(func) %}
+{% match func.throws_type() %}
+{% when Some with (e) %}
+uniffi::call_with_result(call_status, || {
+ let _retval = {% call to_rs_call(func) %}.map_err(Into::into).map_err({{ e|ffi_converter }}::lower)?;
+ Ok({% call ret(func) %})
+})
+{% else %}
+uniffi::call_with_output(call_status, || {
+ {% match func.return_type() -%}
+ {% when Some with (return_type) -%}
+ {{ return_type|ffi_converter }}::lower({% call to_rs_call(func) %})
+ {% else -%}
+ {% if func.full_arguments().is_empty() %}#[allow(clippy::redundant_closure)]{% endif %}
+ {% call to_rs_call(func) %}
+ {% endmatch -%}
+})
+{% endmatch %}
+{% endmacro %}
diff --git a/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs
new file mode 100644
index 0000000000..87414659ae
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs
@@ -0,0 +1,58 @@
+// This file was autogenerated by some hot garbage in the `uniffi` crate.
+// Trust me, you don't want to mess with it!
+{% import "macros.rs" as rs %}
+
+// Check for compatibility between `uniffi` and `uniffi_bindgen` versions.
+// Note that we have an error message on the same line as the assertion.
+// This is important, because if the assertion fails, the compiler only
+// seems to show that single line as context for the user.
+uniffi::assert_compatible_version!("{{ uniffi_version }}"); // Please check that you depend on version {{ uniffi_version }} of the `uniffi` crate.
+
+{% for ty in ci.iter_types() %}
+{%- match ty %}
+{%- when Type::Map with (k, v) -%}
+{# Next comment MUST be after the line to be in the compiler output #}
+uniffi::deps::static_assertions::assert_impl_all!({{ k|type_rs }}: ::std::cmp::Eq, ::std::hash::Hash); // record<{{ k|type_rs }}, {{ v|type_rs }}>
+{%- else %}
+{%- endmatch %}
+{% endfor %}
+
+{% include "RustBuffer.rs" %}
+
+// Error definitions, corresponding to `error` in the UDL.
+{% for e in ci.error_definitions() %}
+{% include "ErrorTemplate.rs" %}
+{% endfor %}
+
+// Enum definitions, corresponding to `enum` in UDL.
+{% for e in ci.enum_definitions() %}
+{% include "EnumTemplate.rs" %}
+{% endfor %}
+
+// Record definitions, implemented as method-less structs, corresponding to `dictionary` objects.
+{% for rec in ci.record_definitions() %}
+{% include "RecordTemplate.rs" %}
+{% endfor %}
+
+// Top level functions, corresponding to UDL `namespace` functions.
+{%- for func in ci.function_definitions() %}
+{% include "TopLevelFunctionTemplate.rs" %}
+{% endfor -%}
+
+// Object definitions, corresponding to UDL `interface` definitions.
+{% for obj in ci.object_definitions() %}
+{% include "ObjectTemplate.rs" %}
+{% endfor %}
+
+// Callback Interface definitions, corresponding to UDL `callback interface` definitions.
+{% for cbi in ci.callback_interface_definitions() %}
+{% include "CallbackInterfaceTemplate.rs" %}
+{% endfor %}
+
+// External and Wrapped types
+{% include "ExternalTypesTemplate.rs" %}
+
+// The `reexport_uniffi_scaffolding` macro
+{% include "ReexportUniFFIScaffolding.rs" %}
+
+{%- import "macros.rs" as rs -%}