summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/backend')
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/config.rs17
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/declarations.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/mod.rs16
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/oracle.rs35
-rw-r--r--third_party/rust/uniffi_bindgen/src/backend/types.rs239
5 files changed, 338 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/backend/config.rs b/third_party/rust/uniffi_bindgen/src/backend/config.rs
new file mode 100644
index 0000000000..616cde0656
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/config.rs
@@ -0,0 +1,17 @@
+/* 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 serde::{Deserialize, Serialize};
+
+/// Config value for template expressions
+///
+/// These are strings that support simple template substitution. `{}` gets replaced by a value.
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct TemplateExpression(String);
+
+impl TemplateExpression {
+ pub fn render(&self, var: &str) -> String {
+ self.0.replace("{}", var)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/backend/declarations.rs b/third_party/rust/uniffi_bindgen/src/backend/declarations.rs
new file mode 100644
index 0000000000..320cfe995c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/declarations.rs
@@ -0,0 +1,31 @@
+/* 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::CodeOracle;
+
+/// A trait that is able to render a declaration about a particular member declared in
+/// the `ComponentInterface`.
+/// Like `CodeType`, it can render declaration code and imports. It also is able to render
+/// code at start-up of the FFI.
+/// All methods are optional, and there is no requirement that the trait be used for a particular
+/// `interface::` member. Thus, it can also be useful for conditionally rendering code.
+pub trait CodeDeclaration {
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Code (one or more statements) that is run on start-up of the library,
+ /// but before the client code has access to it.
+ fn initialization_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
+ None
+ }
+
+ /// Code which represents this member. e.g. the foreign language class definition for
+ /// a given Object type.
+ fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
+ None
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/backend/mod.rs b/third_party/rust/uniffi_bindgen/src/backend/mod.rs
new file mode 100644
index 0000000000..ed4c032508
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/mod.rs
@@ -0,0 +1,16 @@
+/* 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/. */
+
+mod config;
+mod declarations;
+mod oracle;
+mod types;
+
+pub use config::TemplateExpression;
+pub use declarations::CodeDeclaration;
+pub use oracle::CodeOracle;
+pub use types::CodeType;
+
+pub type TypeIdentifier = crate::interface::Type;
+pub type Literal = crate::interface::Literal;
diff --git a/third_party/rust/uniffi_bindgen/src/backend/oracle.rs b/third_party/rust/uniffi_bindgen/src/backend/oracle.rs
new file mode 100644
index 0000000000..9819046584
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/oracle.rs
@@ -0,0 +1,35 @@
+/* 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, TypeIdentifier};
+use crate::interface::FFIType;
+
+/// An object to look up a foreign language code specific renderer for a given type used.
+/// Every `Type` referred to in the `ComponentInterface` should map to a corresponding
+/// `CodeType`.
+///
+/// The mapping may be opaque, but the oracle always knows the answer.
+///
+/// In adddition, the oracle knows how to render identifiers (function names,
+/// class names, variable names etc).
+pub trait CodeOracle {
+ fn find(&self, type_: &TypeIdentifier) -> Box<dyn CodeType>;
+
+ /// Get the idiomatic rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, nm: &str) -> String;
+
+ /// Get the idiomatic rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String;
+
+ /// Get the idiomatic rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String;
+
+ /// Get the idiomatic rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String;
+
+ /// Get the idiomatic rendering of an error name.
+ fn error_name(&self, nm: &str) -> String;
+
+ fn ffi_type_label(&self, ffi_type: &FFIType) -> String;
+}
diff --git a/third_party/rust/uniffi_bindgen/src/backend/types.rs b/third_party/rust/uniffi_bindgen/src/backend/types.rs
new file mode 100644
index 0000000000..aaf5cdaf36
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/backend/types.rs
@@ -0,0 +1,239 @@
+/* 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/. */
+
+//! # Backend traits
+//!
+//! This module provides a number of traits useful for implementing a backend for Uniffi.
+//!
+//! A `CodeType` is needed for each type that will cross the FFI. It should provide helper machinery
+//! in the target language to lift from and lower into a value of that type into a primitive type
+//! (the FFIType), and foreign language expressions that call into the machinery. This helper code
+//! can be provided by a template file.
+//!
+//! A `CodeDeclaration` is needed for each type that is declared in the UDL file. This has access to
+//! the [crate::interface::ComponentInterface], which is the closest thing to an Intermediate Representation.
+//!
+//! `CodeDeclaration`s provide the target language's version of the UDL type, including forwarding method calls
+//! into Rust. It is likely if you're implementing a `CodeDeclaration` for this purpose, it will also need to cross
+//! the FFI, and you'll also need a `CodeType`.
+//!
+//! `CodeDeclaration`s can also be used to conditionally include code: e.g. only include the CallbackInterfaceRuntime
+//! if the user has used at least one callback interface.
+//!
+//! Each backend has a wrapper template for each file it needs to generate. This should collect the `CodeDeclaration`s that
+//! the backend and `ComponentInterface` between them specify and use them to stitch together a file in the target language.
+//!
+//! The `CodeOracle` provides methods to map the `Type` values found in the `ComponentInterface` to the `CodeType`s specified
+//! by the backend. It also provides methods for transforming identifiers into the coding standard for the target language.
+//!
+//! Each backend will have its own `filter` module, which is used by the askama templates used in all `CodeType`s and `CodeDeclaration`s.
+//! This filter provides methods to generate expressions and identifiers in the target language. These are all forwarded to the oracle.
+
+use super::{CodeOracle, Literal};
+use crate::interface::*;
+
+/// A Trait to emit foreign language code to handle referenced types.
+/// A type which is specified in the UDL (i.e. a member of the component interface)
+/// will have a `CodeDeclaration` as well, but for types used e.g. primitive types, Strings, etc
+/// only a `CodeType` is needed.
+pub trait CodeType {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ self.type_label(oracle)
+ }
+
+ /// A representation of the given literal for this type.
+ /// N.B. `Literal` is aliased from `interface::Literal`, so may not be whole suited to this task.
+ fn literal(&self, oracle: &dyn CodeOracle, _literal: &Literal) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(oracle))
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ /// Depending on the binding this will either be a singleton or a class with static methods.
+ ///
+ /// This is the newer way of handling these methods and replaces the lower, write, lift, and
+ /// read CodeType methods. Currently only used by Kotlin, but the plan is to move other
+ /// backends to using this.
+ fn ffi_converter_name(&self, oracle: &dyn CodeOracle) -> String {
+ oracle.class_name(&format!("FfiConverter{}", self.canonical_name(oracle)))
+ }
+
+ /// An expression for lowering a value into something we can pass over the FFI.
+ ///
+ /// N.B. This should align with the `helper_code` generated by this `CodeType`.
+ fn lower(&self, oracle: &dyn CodeOracle, _nm: &str) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(oracle))
+ }
+
+ /// An expression for writing a value into a byte buffer.
+ ///
+ /// N.B. This should align with the `helper_code` generated by this `CodeType`.
+ fn write(&self, oracle: &dyn CodeOracle, _nm: &str, _target: &str) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(oracle))
+ }
+
+ /// An expression for lifting a value from something we received over the FFI.
+ ///
+ /// N.B. This should align with the `helper_code` generated by this `CodeType`.
+ fn lift(&self, oracle: &dyn CodeOracle, _nm: &str) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(oracle))
+ }
+
+ /// An expression for reading a value from a byte buffer.
+ ///
+ /// N.B. This should align with the `helper_code` generated by this `CodeType`.
+ fn read(&self, oracle: &dyn CodeOracle, _nm: &str) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(oracle))
+ }
+
+ /// The lift/lower/read/write methods above must be producing expressions that
+ /// can be part of a larger statement. Most of the time, that is a function call
+ /// to do the work for it.
+ /// The functions being called by those experessions should be declared in the
+ /// helper code generated by this method.
+ fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
+ None
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self, _oracle: &dyn CodeOracle) -> Option<String> {
+ None
+ }
+
+ /// An expression to coerce the given variable to the expected type.
+ fn coerce(&self, oracle: &dyn CodeOracle, _nm: &str) -> String {
+ panic!("Unimplemented for {}", self.type_label(oracle));
+ }
+}
+
+/// This trait is used to implement `CodeType` for `Type` and type-like structs (`Record`, `Enum`, `Field`,
+/// etc). We forward all method calls to a `Box<dyn CodeType>`, which we get by calling
+/// `CodeOracle.find()`.
+pub trait CodeTypeDispatch {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType>;
+}
+
+impl CodeTypeDispatch for Type {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(self)
+ }
+}
+
+impl CodeTypeDispatch for Record {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(&self.type_())
+ }
+}
+
+impl CodeTypeDispatch for Enum {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(&self.type_())
+ }
+}
+
+impl CodeTypeDispatch for Error {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(&self.type_())
+ }
+}
+
+impl CodeTypeDispatch for Object {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(&self.type_())
+ }
+}
+
+impl CodeTypeDispatch for CallbackInterface {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(&self.type_())
+ }
+}
+
+impl CodeTypeDispatch for Field {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(self.type_())
+ }
+}
+
+impl CodeTypeDispatch for Argument {
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ oracle.find(self.type_())
+ }
+}
+
+// Needed to handle &&Type and &&&Type values, which we sometimes end up with in the template code
+impl<T, C> CodeTypeDispatch for T
+where
+ T: std::ops::Deref<Target = C>,
+ C: CodeTypeDispatch,
+{
+ fn code_type_impl(&self, oracle: &dyn CodeOracle) -> Box<dyn CodeType> {
+ self.deref().code_type_impl(oracle)
+ }
+}
+
+impl<T: CodeTypeDispatch> CodeType for T {
+ // The above code implements `CodeTypeDispatch` for `Type` and type-like structs (`Record`,
+ // `Enum`, `Field`, etc). Now we can leverage that to implement `CodeType` for all of them.
+ // This allows for simpler template code (`field|lower` instead of `field.type_()|lower`)
+ fn type_label(&self, oracle: &dyn CodeOracle) -> String {
+ self.code_type_impl(oracle).type_label(oracle)
+ }
+
+ fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
+ self.code_type_impl(oracle).canonical_name(oracle)
+ }
+
+ fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
+ self.code_type_impl(oracle).literal(oracle, literal)
+ }
+
+ fn lower(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
+ self.code_type_impl(oracle).lower(oracle, nm)
+ }
+
+ fn write(&self, oracle: &dyn CodeOracle, nm: &str, target: &str) -> String {
+ self.code_type_impl(oracle).write(oracle, nm, target)
+ }
+
+ fn lift(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
+ self.code_type_impl(oracle).lift(oracle, nm)
+ }
+
+ fn read(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
+ self.code_type_impl(oracle).read(oracle, nm)
+ }
+
+ fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
+ self.code_type_impl(oracle).helper_code(oracle)
+ }
+
+ fn imports(&self, oracle: &dyn CodeOracle) -> Option<Vec<String>> {
+ self.code_type_impl(oracle).imports(oracle)
+ }
+
+ fn initialization_fn(&self, oracle: &dyn CodeOracle) -> Option<String> {
+ self.code_type_impl(oracle).initialization_fn(oracle)
+ }
+
+ fn coerce(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
+ self.code_type_impl(oracle).coerce(oracle, nm)
+ }
+}