summaryrefslogtreecommitdiffstats
path: root/toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs')
-rw-r--r--toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs b/toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs
new file mode 100644
index 0000000000..8a6603aa7d
--- /dev/null
+++ b/toolkit/components/uniffi-bindgen-gecko-js/src/ci_list.rs
@@ -0,0 +1,193 @@
+/* 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/. */
+
+//! Manage the universe of ComponentInterfaces / Configs
+//!
+//! uniffi-bindgen-gecko-js is unique because it generates bindings over a set of UDL files rather
+//! than just one. This is because we want to generate the WebIDL statically rather than generate
+//! it. To accomplish that, each WebIDL function inputs an opaque integer id that identifies which
+//! version of it should run, for example `CallSync` inputs a function id. Operating on all UDL
+//! files at once simplifies the task of ensuring those ids are to be unique and consistent between
+//! the JS and c++ code.
+//!
+//! This module manages the list of ComponentInterface and the object ids.
+
+use crate::render::cpp::ComponentInterfaceCppExt;
+use crate::{Config, ConfigMap};
+use anyhow::{bail, Context, Result};
+use camino::Utf8PathBuf;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use uniffi_bindgen::interface::{CallbackInterface, ComponentInterface, FfiFunction, Object};
+
+pub struct ComponentUniverse {
+ pub components: Vec<(ComponentInterface, Config)>,
+ pub fixture_components: Vec<(ComponentInterface, Config)>,
+}
+
+impl ComponentUniverse {
+ pub fn new(
+ udl_files: Vec<Utf8PathBuf>,
+ fixture_udl_files: Vec<Utf8PathBuf>,
+ config_map: ConfigMap,
+ ) -> Result<Self> {
+ let components = udl_files
+ .into_iter()
+ .map(|udl_file| parse_udl_file(udl_file, &config_map))
+ .collect::<Result<Vec<_>>>()?;
+ let fixture_components = fixture_udl_files
+ .into_iter()
+ .map(|udl_file| parse_udl_file(udl_file, &config_map))
+ .collect::<Result<Vec<_>>>()?;
+ let universe = Self {
+ components,
+ fixture_components,
+ };
+ universe.check_udl_namespaces_unique()?;
+ universe.check_callback_interfaces()?;
+ Ok(universe)
+ }
+
+ fn check_udl_namespaces_unique(&self) -> Result<()> {
+ let mut set = HashSet::new();
+ for ci in self.iter_cis() {
+ if !set.insert(ci.namespace()) {
+ bail!("UDL files have duplicate namespace: {}", ci.namespace());
+ }
+ }
+ Ok(())
+ }
+
+ fn check_callback_interfaces(&self) -> Result<()> {
+ // We don't currently support callback interfaces returning values or throwing errors.
+ for ci in self.iter_cis() {
+ for cbi in ci.callback_interface_definitions() {
+ for method in cbi.methods() {
+ if method.return_type().is_some() {
+ bail!("Callback interface method {}.{} throws an error, which is not yet supported", cbi.name(), method.name())
+ }
+ if method.throws_type().is_some() {
+ bail!("Callback interface method {}.{} returns a value, which is not yet supported", cbi.name(), method.name())
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ pub fn iter_cis(&self) -> impl Iterator<Item = &ComponentInterface> {
+ self.components
+ .iter()
+ .chain(self.fixture_components.iter())
+ .map(|(ci, _)| ci)
+ }
+}
+
+fn parse_udl_file(
+ udl_file: Utf8PathBuf,
+ config_map: &ConfigMap,
+) -> Result<(ComponentInterface, Config)> {
+ let udl = std::fs::read_to_string(&udl_file).context("Error reading UDL file")?;
+ let ci = ComponentInterface::from_webidl(&udl).context("Failed to parse UDL")?;
+ let config = config_map.get(ci.namespace()).cloned().unwrap_or_default();
+ Ok((ci, config))
+}
+
+pub struct FunctionIds<'a> {
+ // Map (CI namespace, func name) -> Ids
+ map: HashMap<(&'a str, &'a str), usize>,
+}
+
+impl<'a> FunctionIds<'a> {
+ pub fn new(cis: &'a ComponentUniverse) -> Self {
+ Self {
+ map: cis
+ .iter_cis()
+ .flat_map(|ci| {
+ ci.exposed_functions()
+ .into_iter()
+ .map(move |f| (ci.namespace(), f.name()))
+ })
+ .enumerate()
+ .map(|(i, (namespace, name))| ((namespace, name), i))
+ // Sort using BTreeSet to guarantee the IDs remain stable across runs
+ .collect::<BTreeSet<_>>()
+ .into_iter()
+ .collect(),
+ }
+ }
+
+ pub fn get(&self, ci: &ComponentInterface, func: &FfiFunction) -> usize {
+ return *self.map.get(&(ci.namespace(), func.name())).unwrap();
+ }
+
+ pub fn name(&self, ci: &ComponentInterface, func: &FfiFunction) -> String {
+ format!("{}:{}", ci.namespace(), func.name())
+ }
+}
+
+pub struct ObjectIds<'a> {
+ // Map (CI namespace, object name) -> Ids
+ map: HashMap<(&'a str, &'a str), usize>,
+}
+
+impl<'a> ObjectIds<'a> {
+ pub fn new(cis: &'a ComponentUniverse) -> Self {
+ Self {
+ map: cis
+ .iter_cis()
+ .flat_map(|ci| {
+ ci.object_definitions()
+ .iter()
+ .map(move |o| (ci.namespace(), o.name()))
+ })
+ .enumerate()
+ .map(|(i, (namespace, name))| ((namespace, name), i))
+ // Sort using BTreeSet to guarantee the IDs remain stable across runs
+ .collect::<BTreeSet<_>>()
+ .into_iter()
+ .collect(),
+ }
+ }
+
+ pub fn get(&self, ci: &ComponentInterface, obj: &Object) -> usize {
+ return *self.map.get(&(ci.namespace(), obj.name())).unwrap();
+ }
+
+ pub fn name(&self, ci: &ComponentInterface, obj: &Object) -> String {
+ format!("{}:{}", ci.namespace(), obj.name())
+ }
+}
+
+pub struct CallbackIds<'a> {
+ // Map (CI namespace, callback name) -> Ids
+ map: HashMap<(&'a str, &'a str), usize>,
+}
+
+impl<'a> CallbackIds<'a> {
+ pub fn new(cis: &'a ComponentUniverse) -> Self {
+ Self {
+ map: cis
+ .iter_cis()
+ .flat_map(|ci| {
+ ci.callback_interface_definitions()
+ .iter()
+ .map(move |cb| (ci.namespace(), cb.name()))
+ })
+ .enumerate()
+ .map(|(i, (namespace, name))| ((namespace, name), i))
+ // Sort using BTreeSet to guarantee the IDs remain stable across runs
+ .collect::<BTreeSet<_>>()
+ .into_iter()
+ .collect(),
+ }
+ }
+
+ pub fn get(&self, ci: &ComponentInterface, cb: &CallbackInterface) -> usize {
+ return *self.map.get(&(ci.namespace(), cb.name())).unwrap();
+ }
+
+ pub fn name(&self, ci: &ComponentInterface, cb: &CallbackInterface) -> String {
+ format!("{}:{}", ci.namespace(), cb.name())
+ }
+}