summaryrefslogtreecommitdiffstats
path: root/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs')
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs190
1 files changed, 190 insertions, 0 deletions
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs b/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
new file mode 100644
index 0000000000..edbe4c7660
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/render/cpp.rs
@@ -0,0 +1,190 @@
+/* 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 crate::{CallbackIds, Config, FunctionIds, ObjectIds};
+use askama::Template;
+use extend::ext;
+use heck::{ToShoutySnakeCase, ToUpperCamelCase};
+use std::collections::HashSet;
+use std::iter;
+use uniffi_bindgen::interface::{
+ CallbackInterface, ComponentInterface, FfiArgument, FfiFunction, FfiType, Object,
+};
+
+#[derive(Template)]
+#[template(path = "UniFFIScaffolding.cpp", escape = "none")]
+pub struct CPPScaffoldingTemplate<'a> {
+ // Prefix for each function name in. This is related to how we handle the test fixtures. For
+ // each function defined in the UniFFI namespace in UniFFI.webidl we:
+ // - Generate a function in to handle it using the real UDL files
+ // - Generate a different function in for handle it using the fixture UDL files
+ // - Have a hand-written stub function that always calls the first function and only calls
+ // the second function in if MOZ_UNIFFI_FIXTURES is defined.
+ pub prefix: &'a str,
+ pub components: &'a Vec<(ComponentInterface, Config)>,
+ pub function_ids: &'a FunctionIds<'a>,
+ pub object_ids: &'a ObjectIds<'a>,
+ pub callback_ids: &'a CallbackIds<'a>,
+}
+
+impl<'a> CPPScaffoldingTemplate<'a> {
+ fn has_any_objects(&self) -> bool {
+ self.components
+ .iter()
+ .any(|(ci, _)| ci.object_definitions().len() > 0)
+ }
+}
+
+// Define extension traits with methods used in our template code
+
+#[ext(name=ComponentInterfaceCppExt)]
+pub impl ComponentInterface {
+ // C++ pointer type name. This needs to be a valid C++ type name and unique across all UDL
+ // files.
+ fn pointer_type(&self, object: &Object) -> String {
+ self._pointer_type(object.name())
+ }
+
+ fn _pointer_type(&self, name: &str) -> String {
+ format!(
+ "k{}{}PointerType",
+ self.namespace().to_upper_camel_case(),
+ name.to_upper_camel_case()
+ )
+ }
+
+ // Iterate over all functions to expose via the UniFFIScaffolding class
+ //
+ // This is basically all the user functions, except we don't expose the free methods for
+ // objects. Freeing is handled by the UniFFIPointer class.
+ //
+ // Note: this function should return `impl Iterator<&FfiFunction>`, but that's not currently
+ // allowed for traits.
+ fn exposed_functions(&self) -> Vec<&FfiFunction> {
+ let excluded: HashSet<_> = self
+ .object_definitions()
+ .iter()
+ .map(|o| o.ffi_object_free().name())
+ .chain(
+ self.callback_interface_definitions()
+ .iter()
+ .map(|cbi| cbi.ffi_init_callback().name()),
+ )
+ .collect();
+ self.iter_user_ffi_function_definitions()
+ .filter(move |f| !excluded.contains(f.name()))
+ .collect()
+ }
+
+ // ScaffoldingConverter class
+ //
+ // This is used to convert types between the JS code and Rust
+ fn scaffolding_converter(&self, ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::RustArcPtr(name) => {
+ format!("ScaffoldingObjectConverter<&{}>", self._pointer_type(name),)
+ }
+ _ => format!("ScaffoldingConverter<{}>", ffi_type.rust_type()),
+ }
+ }
+
+ // ScaffoldingCallHandler class
+ fn scaffolding_call_handler(&self, func: &FfiFunction) -> String {
+ let return_param = match func.return_type() {
+ Some(return_type) => self.scaffolding_converter(return_type),
+ None => "ScaffoldingConverter<void>".to_string(),
+ };
+ let all_params = iter::once(return_param)
+ .chain(
+ func.arguments()
+ .into_iter()
+ .map(|a| self.scaffolding_converter(&a.type_())),
+ )
+ .collect::<Vec<_>>()
+ .join(", ");
+ return format!("ScaffoldingCallHandler<{}>", all_params);
+ }
+}
+
+#[ext(name=FFIFunctionCppExt)]
+pub impl FfiFunction {
+ fn nm(&self) -> String {
+ self.name().to_upper_camel_case()
+ }
+
+ fn rust_name(&self) -> String {
+ self.name().to_string()
+ }
+
+ fn rust_return_type(&self) -> String {
+ match self.return_type() {
+ Some(t) => t.rust_type(),
+ None => "void".to_owned(),
+ }
+ }
+
+ fn rust_arg_list(&self) -> String {
+ let mut parts: Vec<String> = self.arguments().iter().map(|a| a.rust_type()).collect();
+ parts.push("RustCallStatus*".to_owned());
+ parts.join(", ")
+ }
+}
+
+#[ext(name=FFITypeCppExt)]
+pub impl FfiType {
+ // Type for the Rust scaffolding code
+ fn rust_type(&self) -> String {
+ match self {
+ FfiType::UInt8 => "uint8_t",
+ FfiType::Int8 => "int8_t",
+ FfiType::UInt16 => "uint16_t",
+ FfiType::Int16 => "int16_t",
+ FfiType::UInt32 => "uint32_t",
+ FfiType::Int32 => "int32_t",
+ FfiType::UInt64 => "uint64_t",
+ FfiType::Int64 => "int64_t",
+ FfiType::Float32 => "float",
+ FfiType::Float64 => "double",
+ FfiType::RustBuffer(_) => "RustBuffer",
+ FfiType::RustArcPtr(_) => "void *",
+ FfiType::ForeignCallback => "ForeignCallback",
+ FfiType::ForeignBytes => unimplemented!("ForeignBytes not supported"),
+ }
+ .to_owned()
+ }
+}
+
+#[ext(name=FFIArgumentCppExt)]
+pub impl FfiArgument {
+ fn rust_type(&self) -> String {
+ self.type_().rust_type()
+ }
+}
+
+#[ext(name=ObjectCppExt)]
+pub impl Object {
+ fn nm(&self) -> String {
+ self.name().to_upper_camel_case()
+ }
+}
+
+#[ext(name=CallbackInterfaceCppExt)]
+pub impl CallbackInterface {
+ fn nm(&self) -> String {
+ self.name().to_upper_camel_case()
+ }
+
+ /// Name of the static pointer to the JS callback handler
+ fn js_handler(&self) -> String {
+ format!("JS_CALLBACK_HANDLER_{}", self.name().to_shouty_snake_case())
+ }
+
+ /// Name of the C function handler
+ fn c_handler(&self, prefix: &str) -> String {
+ format!(
+ "{prefix}CallbackHandler{}",
+ self.name().to_upper_camel_case()
+ )
+ }
+}