Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_bindgen / src / bindings / kotlin / gen_kotlin / mod.rs
blob1ed0575a9a5175fe399009cf1cb6a4e41f6e13be
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use std::borrow::Borrow;
6 use std::cell::RefCell;
7 use std::collections::{BTreeSet, HashMap, HashSet};
8 use std::fmt::Debug;
10 use anyhow::{Context, Result};
11 use askama::Template;
12 use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
13 use serde::{Deserialize, Serialize};
15 use crate::backend::TemplateExpression;
16 use crate::interface::*;
17 use crate::BindingsConfig;
19 mod callback_interface;
20 mod compounds;
21 mod custom;
22 mod enum_;
23 mod executor;
24 mod external;
25 mod miscellany;
26 mod object;
27 mod primitives;
28 mod record;
29 mod variant;
31 trait CodeType: Debug {
32     /// The language specific label used to reference this type. This will be used in
33     /// method signatures and property declarations.
34     fn type_label(&self, ci: &ComponentInterface) -> String;
36     /// A representation of this type label that can be used as part of another
37     /// identifier. e.g. `read_foo()`, or `FooInternals`.
38     ///
39     /// This is especially useful when creating specialized objects or methods to deal
40     /// with this type only.
41     fn canonical_name(&self) -> String;
43     fn literal(&self, _literal: &Literal, ci: &ComponentInterface) -> String {
44         unimplemented!("Unimplemented for {}", self.type_label(ci))
45     }
47     /// Name of the FfiConverter
48     ///
49     /// This is the object that contains the lower, write, lift, and read methods for this type.
50     /// Depending on the binding this will either be a singleton or a class with static methods.
51     ///
52     /// This is the newer way of handling these methods and replaces the lower, write, lift, and
53     /// read CodeType methods.  Currently only used by Kotlin, but the plan is to move other
54     /// backends to using this.
55     fn ffi_converter_name(&self) -> String {
56         format!("FfiConverter{}", self.canonical_name())
57     }
59     /// A list of imports that are needed if this type is in use.
60     /// Classes are imported exactly once.
61     fn imports(&self) -> Option<Vec<String>> {
62         None
63     }
65     /// Function to run at startup
66     fn initialization_fn(&self) -> Option<String> {
67         None
68     }
71 // config options to customize the generated Kotlin.
72 #[derive(Debug, Default, Clone, Serialize, Deserialize)]
73 pub struct Config {
74     package_name: Option<String>,
75     cdylib_name: Option<String>,
76     #[serde(default)]
77     custom_types: HashMap<String, CustomTypeConfig>,
78     #[serde(default)]
79     external_packages: HashMap<String, String>,
82 #[derive(Debug, Default, Clone, Serialize, Deserialize)]
83 pub struct CustomTypeConfig {
84     imports: Option<Vec<String>>,
85     type_name: Option<String>,
86     into_custom: TemplateExpression,
87     from_custom: TemplateExpression,
90 impl Config {
91     pub fn package_name(&self) -> String {
92         if let Some(package_name) = &self.package_name {
93             package_name.clone()
94         } else {
95             "uniffi".into()
96         }
97     }
99     pub fn cdylib_name(&self) -> String {
100         if let Some(cdylib_name) = &self.cdylib_name {
101             cdylib_name.clone()
102         } else {
103             "uniffi".into()
104         }
105     }
108 impl BindingsConfig for Config {
109     fn update_from_ci(&mut self, ci: &ComponentInterface) {
110         self.package_name
111             .get_or_insert_with(|| format!("uniffi.{}", ci.namespace()));
112         self.cdylib_name
113             .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
114     }
116     fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
117         self.cdylib_name
118             .get_or_insert_with(|| cdylib_name.to_string());
119     }
121     fn update_from_dependency_configs(&mut self, config_map: HashMap<&str, &Self>) {
122         for (crate_name, config) in config_map {
123             if !self.external_packages.contains_key(crate_name) {
124                 self.external_packages
125                     .insert(crate_name.to_string(), config.package_name());
126             }
127         }
128     }
131 // Generate kotlin bindings for the given ComponentInterface, as a string.
132 pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
133     KotlinWrapper::new(config.clone(), ci)
134         .render()
135         .context("failed to render kotlin bindings")
138 /// A struct to record a Kotlin import statement.
139 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
140 pub enum ImportRequirement {
141     /// The name we are importing.
142     Import { name: String },
143     /// Import the name with the specified local name.
144     ImportAs { name: String, as_name: String },
147 impl ImportRequirement {
148     /// Render the Kotlin import statement.
149     fn render(&self) -> String {
150         match &self {
151             ImportRequirement::Import { name } => format!("import {name}"),
152             ImportRequirement::ImportAs { name, as_name } => {
153                 format!("import {name} as {as_name}")
154             }
155         }
156     }
159 /// Renders Kotlin helper code for all types
161 /// This template is a bit different than others in that it stores internal state from the render
162 /// process.  Make sure to only call `render()` once.
163 #[derive(Template)]
164 #[template(syntax = "kt", escape = "none", path = "Types.kt")]
165 pub struct TypeRenderer<'a> {
166     config: &'a Config,
167     ci: &'a ComponentInterface,
168     // Track included modules for the `include_once()` macro
169     include_once_names: RefCell<HashSet<String>>,
170     // Track imports added with the `add_import()` macro
171     imports: RefCell<BTreeSet<ImportRequirement>>,
174 impl<'a> TypeRenderer<'a> {
175     fn new(config: &'a Config, ci: &'a ComponentInterface) -> Self {
176         Self {
177             config,
178             ci,
179             include_once_names: RefCell::new(HashSet::new()),
180             imports: RefCell::new(BTreeSet::new()),
181         }
182     }
184     // Get the package name for an external type
185     fn external_type_package_name(&self, module_path: &str, namespace: &str) -> String {
186         // config overrides are keyed by the crate name, default fallback is the namespace.
187         let crate_name = module_path.split("::").next().unwrap();
188         match self.config.external_packages.get(crate_name) {
189             Some(name) => name.clone(),
190             // unreachable in library mode - all deps are in our config with correct namespace.
191             None => format!("uniffi.{namespace}"),
192         }
193     }
195     // The following methods are used by the `Types.kt` macros.
197     // Helper for the including a template, but only once.
198     //
199     // The first time this is called with a name it will return true, indicating that we should
200     // include the template.  Subsequent calls will return false.
201     fn include_once_check(&self, name: &str) -> bool {
202         self.include_once_names
203             .borrow_mut()
204             .insert(name.to_string())
205     }
207     // Helper to add an import statement
208     //
209     // Call this inside your template to cause an import statement to be added at the top of the
210     // file.  Imports will be sorted and de-deuped.
211     //
212     // Returns an empty string so that it can be used inside an askama `{{ }}` block.
213     fn add_import(&self, name: &str) -> &str {
214         self.imports.borrow_mut().insert(ImportRequirement::Import {
215             name: name.to_owned(),
216         });
217         ""
218     }
220     // Like add_import, but arranges for `import name as as_name`
221     fn add_import_as(&self, name: &str, as_name: &str) -> &str {
222         self.imports
223             .borrow_mut()
224             .insert(ImportRequirement::ImportAs {
225                 name: name.to_owned(),
226                 as_name: as_name.to_owned(),
227             });
228         ""
229     }
232 #[derive(Template)]
233 #[template(syntax = "kt", escape = "none", path = "wrapper.kt")]
234 pub struct KotlinWrapper<'a> {
235     config: Config,
236     ci: &'a ComponentInterface,
237     type_helper_code: String,
238     type_imports: BTreeSet<ImportRequirement>,
239     has_async_fns: bool,
242 impl<'a> KotlinWrapper<'a> {
243     pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
244         let type_renderer = TypeRenderer::new(&config, ci);
245         let type_helper_code = type_renderer.render().unwrap();
246         let type_imports = type_renderer.imports.into_inner();
247         Self {
248             config,
249             ci,
250             type_helper_code,
251             type_imports,
252             has_async_fns: ci.has_async_fns(),
253         }
254     }
256     pub fn initialization_fns(&self) -> Vec<String> {
257         self.ci
258             .iter_types()
259             .map(|t| KotlinCodeOracle.find(t))
260             .filter_map(|ct| ct.initialization_fn())
261             .chain(
262                 self.has_async_fns
263                     .then(|| "uniffiRustFutureContinuationCallback.register".into()),
264             )
265             .collect()
266     }
268     pub fn imports(&self) -> Vec<ImportRequirement> {
269         self.type_imports.iter().cloned().collect()
270     }
273 #[derive(Clone)]
274 pub struct KotlinCodeOracle;
276 impl KotlinCodeOracle {
277     fn find(&self, type_: &Type) -> Box<dyn CodeType> {
278         type_.clone().as_type().as_codetype()
279     }
281     /// Get the idiomatic Kotlin rendering of a class name (for enums, records, errors, etc).
282     fn class_name(&self, ci: &ComponentInterface, nm: &str) -> String {
283         let name = nm.to_string().to_upper_camel_case();
284         // fixup errors.
285         ci.is_name_used_as_error(nm)
286             .then(|| self.convert_error_suffix(&name))
287             .unwrap_or(name)
288     }
290     fn convert_error_suffix(&self, nm: &str) -> String {
291         match nm.strip_suffix("Error") {
292             None => nm.to_string(),
293             Some(stripped) => format!("{stripped}Exception"),
294         }
295     }
297     /// Get the idiomatic Kotlin rendering of a function name.
298     fn fn_name(&self, nm: &str) -> String {
299         format!("`{}`", nm.to_string().to_lower_camel_case())
300     }
302     /// Get the idiomatic Kotlin rendering of a variable name.
303     fn var_name(&self, nm: &str) -> String {
304         format!("`{}`", nm.to_string().to_lower_camel_case())
305     }
307     /// Get the idiomatic Kotlin rendering of an individual enum variant.
308     fn enum_variant_name(&self, nm: &str) -> String {
309         nm.to_string().to_shouty_snake_case()
310     }
312     fn ffi_type_label_by_value(ffi_type: &FfiType) -> String {
313         match ffi_type {
314             FfiType::RustBuffer(_) => format!("{}.ByValue", Self::ffi_type_label(ffi_type)),
315             _ => Self::ffi_type_label(ffi_type),
316         }
317     }
319     fn ffi_type_label(ffi_type: &FfiType) -> String {
320         match ffi_type {
321             // Note that unsigned integers in Kotlin are currently experimental, but java.nio.ByteBuffer does not
322             // support them yet. Thus, we use the signed variants to represent both signed and unsigned
323             // types from the component API.
324             FfiType::Int8 | FfiType::UInt8 => "Byte".to_string(),
325             FfiType::Int16 | FfiType::UInt16 => "Short".to_string(),
326             FfiType::Int32 | FfiType::UInt32 => "Int".to_string(),
327             FfiType::Int64 | FfiType::UInt64 => "Long".to_string(),
328             FfiType::Float32 => "Float".to_string(),
329             FfiType::Float64 => "Double".to_string(),
330             FfiType::RustArcPtr(_) => "Pointer".to_string(),
331             FfiType::RustBuffer(maybe_suffix) => {
332                 format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default())
333             }
334             FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
335             FfiType::ForeignCallback => "ForeignCallback".to_string(),
336             FfiType::ForeignExecutorHandle => "USize".to_string(),
337             FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback".to_string(),
338             FfiType::RustFutureHandle => "Pointer".to_string(),
339             FfiType::RustFutureContinuationCallback => {
340                 "UniFffiRustFutureContinuationCallbackType".to_string()
341             }
342             FfiType::RustFutureContinuationData => "USize".to_string(),
343         }
344     }
347 trait AsCodeType {
348     fn as_codetype(&self) -> Box<dyn CodeType>;
351 impl<T: AsType> AsCodeType for T {
352     fn as_codetype(&self) -> Box<dyn CodeType> {
353         // Map `Type` instances to a `Box<dyn CodeType>` for that type.
354         //
355         // There is a companion match in `templates/Types.kt` which performs a similar function for the
356         // template code.
357         //
358         //   - When adding additional types here, make sure to also add a match arm to the `Types.kt` template.
359         //   - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
360         match self.as_type() {
361             Type::UInt8 => Box::new(primitives::UInt8CodeType),
362             Type::Int8 => Box::new(primitives::Int8CodeType),
363             Type::UInt16 => Box::new(primitives::UInt16CodeType),
364             Type::Int16 => Box::new(primitives::Int16CodeType),
365             Type::UInt32 => Box::new(primitives::UInt32CodeType),
366             Type::Int32 => Box::new(primitives::Int32CodeType),
367             Type::UInt64 => Box::new(primitives::UInt64CodeType),
368             Type::Int64 => Box::new(primitives::Int64CodeType),
369             Type::Float32 => Box::new(primitives::Float32CodeType),
370             Type::Float64 => Box::new(primitives::Float64CodeType),
371             Type::Boolean => Box::new(primitives::BooleanCodeType),
372             Type::String => Box::new(primitives::StringCodeType),
373             Type::Bytes => Box::new(primitives::BytesCodeType),
375             Type::Timestamp => Box::new(miscellany::TimestampCodeType),
376             Type::Duration => Box::new(miscellany::DurationCodeType),
378             Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
379             Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
380             Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
381             Type::CallbackInterface { name, .. } => {
382                 Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
383             }
384             Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
385             Type::Optional { inner_type } => {
386                 Box::new(compounds::OptionalCodeType::new(*inner_type))
387             }
388             Type::Sequence { inner_type } => {
389                 Box::new(compounds::SequenceCodeType::new(*inner_type))
390             }
391             Type::Map {
392                 key_type,
393                 value_type,
394             } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
395             Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
396             Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
397         }
398     }
401 mod filters {
402     use super::*;
403     pub use crate::backend::filters::*;
405     pub(super) fn type_name(
406         as_ct: &impl AsCodeType,
407         ci: &ComponentInterface,
408     ) -> Result<String, askama::Error> {
409         Ok(as_ct.as_codetype().type_label(ci))
410     }
412     pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
413         Ok(as_ct.as_codetype().canonical_name())
414     }
416     pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
417         Ok(as_ct.as_codetype().ffi_converter_name())
418     }
420     pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
421         Ok(format!(
422             "{}.lower",
423             as_ct.as_codetype().ffi_converter_name()
424         ))
425     }
427     pub(super) fn allocation_size_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
428         Ok(format!(
429             "{}.allocationSize",
430             as_ct.as_codetype().ffi_converter_name()
431         ))
432     }
434     pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
435         Ok(format!(
436             "{}.write",
437             as_ct.as_codetype().ffi_converter_name()
438         ))
439     }
441     pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
442         Ok(format!("{}.lift", as_ct.as_codetype().ffi_converter_name()))
443     }
445     pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
446         Ok(format!("{}.read", as_ct.as_codetype().ffi_converter_name()))
447     }
449     pub fn render_literal(
450         literal: &Literal,
451         as_ct: &impl AsType,
452         ci: &ComponentInterface,
453     ) -> Result<String, askama::Error> {
454         Ok(as_ct.as_codetype().literal(literal, ci))
455     }
457     pub fn ffi_type_name_by_value(type_: &FfiType) -> Result<String, askama::Error> {
458         Ok(KotlinCodeOracle::ffi_type_label_by_value(type_))
459     }
461     /// Get the idiomatic Kotlin rendering of a function name.
462     pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
463         Ok(KotlinCodeOracle.fn_name(nm))
464     }
466     /// Get the idiomatic Kotlin rendering of a variable name.
467     pub fn var_name(nm: &str) -> Result<String, askama::Error> {
468         Ok(KotlinCodeOracle.var_name(nm))
469     }
471     /// Get a String representing the name used for an individual enum variant.
472     pub fn variant_name(v: &Variant) -> Result<String, askama::Error> {
473         Ok(KotlinCodeOracle.enum_variant_name(v.name()))
474     }
476     pub fn error_variant_name(v: &Variant) -> Result<String, askama::Error> {
477         let name = v.name().to_string().to_upper_camel_case();
478         Ok(KotlinCodeOracle.convert_error_suffix(&name))
479     }
481     pub fn async_poll(
482         callable: impl Callable,
483         ci: &ComponentInterface,
484     ) -> Result<String, askama::Error> {
485         let ffi_func = callable.ffi_rust_future_poll(ci);
486         Ok(format!(
487             "{{ future, continuation -> _UniFFILib.INSTANCE.{ffi_func}(future, continuation) }}"
488         ))
489     }
491     pub fn async_complete(
492         callable: impl Callable,
493         ci: &ComponentInterface,
494     ) -> Result<String, askama::Error> {
495         let ffi_func = callable.ffi_rust_future_complete(ci);
496         let call = format!("_UniFFILib.INSTANCE.{ffi_func}(future, continuation)");
497         let call = match callable.return_type() {
498             Some(Type::External {
499                 kind: ExternalKind::DataClass,
500                 name,
501                 ..
502             }) => {
503                 // Need to convert the RustBuffer from our package to the RustBuffer of the external package
504                 let suffix = KotlinCodeOracle.class_name(ci, &name);
505                 format!("{call}.let {{ RustBuffer{suffix}.create(it.capacity, it.len, it.data) }}")
506             }
507             _ => call,
508         };
509         Ok(format!("{{ future, continuation -> {call} }}"))
510     }
512     pub fn async_free(
513         callable: impl Callable,
514         ci: &ComponentInterface,
515     ) -> Result<String, askama::Error> {
516         let ffi_func = callable.ffi_rust_future_free(ci);
517         Ok(format!(
518             "{{ future -> _UniFFILib.INSTANCE.{ffi_func}(future) }}"
519         ))
520     }
522     /// Remove the "`" chars we put around function/variable names
523     ///
524     /// These are used to avoid name clashes with kotlin identifiers, but sometimes you want to
525     /// render the name unquoted.  One example is the message property for errors where we want to
526     /// display the name for the user.
527     pub fn unquote(nm: &str) -> Result<String, askama::Error> {
528         Ok(nm.trim_matches('`').to_string())
529     }