diff --git a/core/src/ast/enums.rs b/core/src/ast/enums.rs new file mode 100644 index 000000000..5bbd84eb4 --- /dev/null +++ b/core/src/ast/enums.rs @@ -0,0 +1,85 @@ +use serde::{Deserialize, Serialize}; + +use super::utils::get_doc_lines; +use super::Method; + +/// A fieldless enum declaration in an FFI module. +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Enum { + pub name: String, + pub doc_lines: String, + /// A list of variants of the enum. (name, discriminant, doc_lines) + pub variants: Vec<(String, isize, String)>, + pub methods: Vec, +} + +impl From<&syn::ItemEnum> for Enum { + /// Extract an [`Enum`] metadata value from an AST node. + fn from(strct: &syn::ItemEnum) -> Enum { + let mut last_discriminant = -1; + Enum { + name: strct.ident.to_string(), + doc_lines: get_doc_lines(&strct.attrs), + variants: strct + .variants + .iter() + .map(|v| { + let new_discriminant = v + .discriminant + .as_ref() + .map(|d| { + if let syn::Expr::Lit(syn::ExprLit { + attrs: _, + lit: syn::Lit::Int(lit_int), + }) = &d.1 + { + lit_int.base10_parse::().unwrap() + } else { + panic!("Expected a discriminant to be a constant integer"); + } + }) + .unwrap_or_else(|| last_discriminant + 1); + + last_discriminant = new_discriminant; + + ( + v.ident.to_string(), + new_discriminant, + get_doc_lines(&v.attrs), + ) + }) + .collect(), + methods: vec![], + } + } +} + +#[cfg(test)] +mod tests { + use insta::{self, Settings}; + + use quote::quote; + use syn; + + use super::Enum; + + #[test] + fn simple_enum() { + let mut settings = Settings::new(); + settings.set_sort_maps(true); + + settings.bind(|| { + insta::assert_yaml_snapshot!(Enum::from( + &syn::parse2(quote! { + /// Some docs. + enum MyLocalEnum { + Abc, + /// Some more docs. + Def + } + }) + .unwrap() + )); + }); + } +} diff --git a/core/src/ast/mod.rs b/core/src/ast/mod.rs index c1bb57b8f..265c5af96 100644 --- a/core/src/ast/mod.rs +++ b/core/src/ast/mod.rs @@ -11,6 +11,9 @@ pub use modules::{File, Module}; mod structs; pub use structs::{OpaqueStruct, Struct}; +mod enums; +pub use enums::Enum; + mod types; pub use types::{CustomType, ModSymbol, PrimitiveType, TypeName}; diff --git a/core/src/ast/modules.rs b/core/src/ast/modules.rs index 599a6ecd7..bd5964e2a 100644 --- a/core/src/ast/modules.rs +++ b/core/src/ast/modules.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use quote::ToTokens; use syn::{ImplItem, Item, ItemMod, UseTree}; -use super::{CustomType, Method, ModSymbol, OpaqueStruct, Path, Struct, TypeName}; +use super::{CustomType, Enum, Method, ModSymbol, OpaqueStruct, Path, Struct, TypeName}; #[derive(Clone, Serialize, Deserialize, Debug)] pub struct Module { @@ -99,6 +99,14 @@ impl Module { } } } + + Item::Enum(enm) => { + if analyze_types { + custom_types_by_name + .insert(enm.ident.to_string(), CustomType::Enum(Enum::from(enm))); + } + } + Item::Impl(ipl) => { if analyze_types { assert!(ipl.trait_.is_none()); @@ -126,6 +134,9 @@ impl Module { CustomType::Opaque(strct) => { strct.methods.append(&mut new_methods); } + CustomType::Enum(enm) => { + enm.methods.append(&mut new_methods); + } } } } diff --git a/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap new file mode 100644 index 000000000..351d41665 --- /dev/null +++ b/core/src/ast/snapshots/diplomat_core__ast__enums__tests__simple_enum.snap @@ -0,0 +1,16 @@ +--- +source: core/src/ast/enums.rs +expression: "Enum::from(&syn::parse2(quote! {\n /// Some docs.\n enum MyLocalEnum\n {\n Abc, /// Some more docs.\n Def\n }\n }).unwrap())" + +--- +name: MyLocalEnum +doc_lines: Some docs. +variants: + - - Abc + - 0 + - "" + - - Def + - 1 + - Some more docs. +methods: [] + diff --git a/core/src/ast/types.rs b/core/src/ast/types.rs index 861664281..d07a618ba 100644 --- a/core/src/ast/types.rs +++ b/core/src/ast/types.rs @@ -7,16 +7,17 @@ use lazy_static::lazy_static; use std::collections::HashMap; use std::iter::FromIterator; -use super::{Method, OpaqueStruct, Path, Struct}; +use super::{Enum, Method, OpaqueStruct, Path, Struct}; /// A type declared inside a Diplomat-annotated module. #[derive(Clone, Serialize, Deserialize, Debug)] pub enum CustomType { /// A non-opaque struct whose fields will be visible across the FFI boundary. Struct(Struct), - // TODO(shadaj): Enum /// A struct annotated with [`diplomat::opaque`] whose fields are not visible. Opaque(OpaqueStruct), + /// A fieldless enum. + Enum(Enum), } impl CustomType { @@ -25,6 +26,7 @@ impl CustomType { match self { CustomType::Struct(strct) => &strct.name, CustomType::Opaque(strct) => &strct.name, + CustomType::Enum(enm) => &enm.name, } } @@ -33,6 +35,7 @@ impl CustomType { match self { CustomType::Struct(strct) => &strct.methods, CustomType::Opaque(strct) => &strct.methods, + CustomType::Enum(enm) => &enm.methods, } } @@ -41,6 +44,7 @@ impl CustomType { match self { CustomType::Struct(strct) => &strct.doc_lines, CustomType::Opaque(strct) => &strct.doc_lines, + CustomType::Enum(enm) => &enm.doc_lines, } } @@ -61,6 +65,7 @@ impl CustomType { } } CustomType::Opaque(_) => {} + CustomType::Enum(_) => {} } for method in self.methods().iter() { diff --git a/example/c/api.h b/example/c/api.h index e7b6bfc31..13e3b2b09 100644 --- a/example/c/api.h +++ b/example/c/api.h @@ -13,11 +13,26 @@ typedef struct ICU4XFixedDecimal ICU4XFixedDecimal; typedef struct ICU4XFixedDecimalFormat ICU4XFixedDecimalFormat; +enum ICU4XFixedDecimalGroupingStrategy { + ICU4XFixedDecimalGroupingStrategy_Auto = 0, + ICU4XFixedDecimalGroupingStrategy_Never = 1, + ICU4XFixedDecimalGroupingStrategy_Always = 2, + ICU4XFixedDecimalGroupingStrategy_Min2 = 3, +}; + +enum ICU4XFixedDecimalSignDisplay { + ICU4XFixedDecimalSignDisplay_Auto = 0, + ICU4XFixedDecimalSignDisplay_Never = 1, + ICU4XFixedDecimalSignDisplay_Always = 2, + ICU4XFixedDecimalSignDisplay_ExceptZero = 3, + ICU4XFixedDecimalSignDisplay_Negative = 4, +}; + typedef struct ICU4XLocale ICU4XLocale; typedef struct ICU4XFixedDecimalFormatOptions { - uint8_t grouping_strategy; - uint8_t sign_display; + ssize_t grouping_strategy; + ssize_t sign_display; } ICU4XFixedDecimalFormatOptions; typedef struct ICU4XFixedDecimalFormatResult { @@ -45,6 +60,8 @@ void ICU4XFixedDecimalFormat_destroy(ICU4XFixedDecimalFormat* self); ICU4XFixedDecimalFormatOptions ICU4XFixedDecimalFormatOptions_default(); void ICU4XFixedDecimalFormatOptions_destroy(ICU4XFixedDecimalFormatOptions* self); void ICU4XFixedDecimalFormatResult_destroy(ICU4XFixedDecimalFormatResult* self); +void ICU4XFixedDecimalGroupingStrategy_destroy(ssize_t* self); +void ICU4XFixedDecimalSignDisplay_destroy(ssize_t* self); ICU4XLocale* ICU4XLocale_new(const char* name_data, size_t name_len); void ICU4XLocale_destroy(ICU4XLocale* self); diff --git a/example/cpp/api.h b/example/cpp/api.h index e7b6bfc31..13e3b2b09 100644 --- a/example/cpp/api.h +++ b/example/cpp/api.h @@ -13,11 +13,26 @@ typedef struct ICU4XFixedDecimal ICU4XFixedDecimal; typedef struct ICU4XFixedDecimalFormat ICU4XFixedDecimalFormat; +enum ICU4XFixedDecimalGroupingStrategy { + ICU4XFixedDecimalGroupingStrategy_Auto = 0, + ICU4XFixedDecimalGroupingStrategy_Never = 1, + ICU4XFixedDecimalGroupingStrategy_Always = 2, + ICU4XFixedDecimalGroupingStrategy_Min2 = 3, +}; + +enum ICU4XFixedDecimalSignDisplay { + ICU4XFixedDecimalSignDisplay_Auto = 0, + ICU4XFixedDecimalSignDisplay_Never = 1, + ICU4XFixedDecimalSignDisplay_Always = 2, + ICU4XFixedDecimalSignDisplay_ExceptZero = 3, + ICU4XFixedDecimalSignDisplay_Negative = 4, +}; + typedef struct ICU4XLocale ICU4XLocale; typedef struct ICU4XFixedDecimalFormatOptions { - uint8_t grouping_strategy; - uint8_t sign_display; + ssize_t grouping_strategy; + ssize_t sign_display; } ICU4XFixedDecimalFormatOptions; typedef struct ICU4XFixedDecimalFormatResult { @@ -45,6 +60,8 @@ void ICU4XFixedDecimalFormat_destroy(ICU4XFixedDecimalFormat* self); ICU4XFixedDecimalFormatOptions ICU4XFixedDecimalFormatOptions_default(); void ICU4XFixedDecimalFormatOptions_destroy(ICU4XFixedDecimalFormatOptions* self); void ICU4XFixedDecimalFormatResult_destroy(ICU4XFixedDecimalFormatResult* self); +void ICU4XFixedDecimalGroupingStrategy_destroy(ssize_t* self); +void ICU4XFixedDecimalSignDisplay_destroy(ssize_t* self); ICU4XLocale* ICU4XLocale_new(const char* name_data, size_t name_len); void ICU4XLocale_destroy(ICU4XLocale* self); diff --git a/example/cpp/api.hpp b/example/cpp/api.hpp index 8ccbc8160..8b95d40b7 100644 --- a/example/cpp/api.hpp +++ b/example/cpp/api.hpp @@ -17,6 +17,21 @@ struct ICU4XFixedDecimalFormatOptions; struct ICU4XFixedDecimalFormatResult; +enum struct ICU4XFixedDecimalGroupingStrategy : ssize_t { + Auto = 0, + Never = 1, + Always = 2, + Min2 = 3, +}; + +enum struct ICU4XFixedDecimalSignDisplay : ssize_t { + Auto = 0, + Never = 1, + Always = 2, + ExceptZero = 3, + Negative = 4, +}; + class ICU4XLocale; struct ICU4XDataProviderDeleter { @@ -86,8 +101,8 @@ struct ICU4XFixedDecimalFormatOptionsDeleter { }; struct ICU4XFixedDecimalFormatOptions { public: - uint8_t grouping_strategy; - uint8_t sign_display; + ICU4XFixedDecimalGroupingStrategy grouping_strategy; + ICU4XFixedDecimalSignDisplay sign_display; static ICU4XFixedDecimalFormatOptions default_(); }; @@ -124,7 +139,7 @@ std::string ICU4XFixedDecimal::to_string() { ICU4XFixedDecimalFormatResult ICU4XFixedDecimalFormat::try_new(const ICU4XLocale& locale, const ICU4XDataProvider& provider, ICU4XFixedDecimalFormatOptions options) { ICU4XFixedDecimalFormatOptions diplomat_wrapped_struct_options = options; - capi::ICU4XFixedDecimalFormatResult diplomat_raw_struct_out_value = capi::ICU4XFixedDecimalFormat_try_new(locale.AsFFI(), provider.AsFFI(), capi::ICU4XFixedDecimalFormatOptions{ .grouping_strategy = diplomat_wrapped_struct_options.grouping_strategy, .sign_display = diplomat_wrapped_struct_options.sign_display }); + capi::ICU4XFixedDecimalFormatResult diplomat_raw_struct_out_value = capi::ICU4XFixedDecimalFormat_try_new(locale.AsFFI(), provider.AsFFI(), capi::ICU4XFixedDecimalFormatOptions{ .grouping_strategy = static_cast(diplomat_wrapped_struct_options.grouping_strategy), .sign_display = static_cast(diplomat_wrapped_struct_options.sign_display) }); auto diplomat_optional_raw_out_value_fdf = diplomat_raw_struct_out_value.fdf; std::optional diplomat_optional_out_value_fdf; if (diplomat_optional_raw_out_value_fdf != nullptr) { @@ -143,10 +158,12 @@ std::string ICU4XFixedDecimalFormat::format_write(const ICU4XFixedDecimal& value ICU4XFixedDecimalFormatOptions ICU4XFixedDecimalFormatOptions::default_() { capi::ICU4XFixedDecimalFormatOptions diplomat_raw_struct_out_value = capi::ICU4XFixedDecimalFormatOptions_default(); - return ICU4XFixedDecimalFormatOptions{ .grouping_strategy = std::move(diplomat_raw_struct_out_value.grouping_strategy), .sign_display = std::move(diplomat_raw_struct_out_value.sign_display) }; + return ICU4XFixedDecimalFormatOptions{ .grouping_strategy = std::move(ICU4XFixedDecimalGroupingStrategy{ diplomat_raw_struct_out_value.grouping_strategy }), .sign_display = std::move(ICU4XFixedDecimalSignDisplay{ diplomat_raw_struct_out_value.sign_display }) }; } + + ICU4XLocale ICU4XLocale::new_(const std::string name) { return ICU4XLocale(capi::ICU4XLocale_new(name.data(), name.length())); } diff --git a/example/js/api.mjs b/example/js/api.mjs index 0a662e5d7..0f8a5ae63 100644 --- a/example/js/api.mjs +++ b/example/js/api.mjs @@ -75,7 +75,7 @@ export class ICU4XFixedDecimalFormat { const diplomat_ICU4XFixedDecimalFormatOptions_extracted_sign_display = options["sign_display"]; const diplomat_out = (() => { const diplomat_receive_buffer = wasm.diplomat_alloc(5); - wasm.ICU4XFixedDecimalFormat_try_new(diplomat_receive_buffer, locale.underlying, provider.underlying, diplomat_ICU4XFixedDecimalFormatOptions_extracted_grouping_strategy, diplomat_ICU4XFixedDecimalFormatOptions_extracted_sign_display); + wasm.ICU4XFixedDecimalFormat_try_new(diplomat_receive_buffer, locale.underlying, provider.underlying, ICU4XFixedDecimalGroupingStrategy_js_to_rust[diplomat_ICU4XFixedDecimalFormatOptions_extracted_grouping_strategy], ICU4XFixedDecimalSignDisplay_js_to_rust[diplomat_ICU4XFixedDecimalFormatOptions_extracted_sign_display]); const out = new ICU4XFixedDecimalFormatResult(diplomat_receive_buffer); const out_fdf_value = out.fdf; ICU4XFixedDecimalFormat_box_destroy_registry.register(out_fdf_value, out_fdf_value.underlying); @@ -106,12 +106,12 @@ export class ICU4XFixedDecimalFormatOptions { static default() { const diplomat_out = (() => { - const diplomat_receive_buffer = wasm.diplomat_alloc(2); + const diplomat_receive_buffer = wasm.diplomat_alloc(8); wasm.ICU4XFixedDecimalFormatOptions_default(diplomat_receive_buffer); const out = new ICU4XFixedDecimalFormatOptions(diplomat_receive_buffer); diplomat_alloc_destroy_registry.register(out, { ptr: out.underlying, - size: 2 + size: 8 }); return out; })(); @@ -119,11 +119,11 @@ export class ICU4XFixedDecimalFormatOptions { } get grouping_strategy() { - return (new Uint8Array(wasm.memory.buffer, this.underlying + 0, 1))[0]; + return ICU4XFixedDecimalGroupingStrategy_rust_to_js[(new Int32Array(wasm.memory.buffer, this.underlying + 0, 1))[0]]; } get sign_display() { - return (new Uint8Array(wasm.memory.buffer, this.underlying + 1, 1))[0]; + return ICU4XFixedDecimalSignDisplay_rust_to_js[(new Int32Array(wasm.memory.buffer, this.underlying + 4, 1))[0]]; } } @@ -148,6 +148,34 @@ export class ICU4XFixedDecimalFormatResult { } } +const ICU4XFixedDecimalGroupingStrategy_js_to_rust = { + "Auto": 0, + "Never": 1, + "Always": 2, + "Min2": 3, +}; +const ICU4XFixedDecimalGroupingStrategy_rust_to_js = { + 0: "Auto", + 1: "Never", + 2: "Always", + 3: "Min2", +}; + +const ICU4XFixedDecimalSignDisplay_js_to_rust = { + "Auto": 0, + "Never": 1, + "Always": 2, + "ExceptZero": 3, + "Negative": 4, +}; +const ICU4XFixedDecimalSignDisplay_rust_to_js = { + 0: "Auto", + 1: "Never", + 2: "Always", + 3: "ExceptZero", + 4: "Negative", +}; + const ICU4XLocale_box_destroy_registry = new FinalizationRegistry(underlying => { wasm.ICU4XLocale_destroy(underlying); }); diff --git a/example/js/docs/index.rst b/example/js/docs/index.rst index b05e0d24b..7501dbe6c 100644 --- a/example/js/docs/index.rst +++ b/example/js/docs/index.rst @@ -71,6 +71,14 @@ Whether creating the :js:class:`ICU4XFixedDecimalFormat` was successful. +.. js:class:: ICU4XFixedDecimalGroupingStrategy + + + +.. js:class:: ICU4XFixedDecimalSignDisplay + + + .. js:class:: ICU4XLocale diff --git a/example/src/decimal.rs b/example/src/decimal.rs index d07a16383..99344d4bc 100644 --- a/example/src/decimal.rs +++ b/example/src/decimal.rs @@ -23,16 +23,31 @@ pub mod ffi { pub success: bool, } + pub enum ICU4XFixedDecimalGroupingStrategy { + Auto, + Never, + Always, + Min2, + } + + pub enum ICU4XFixedDecimalSignDisplay { + Auto, + Never, + Always, + ExceptZero, + Negative, + } + pub struct ICU4XFixedDecimalFormatOptions { - pub grouping_strategy: u8, - pub sign_display: u8, + pub grouping_strategy: ICU4XFixedDecimalGroupingStrategy, + pub sign_display: ICU4XFixedDecimalSignDisplay, } impl ICU4XFixedDecimalFormatOptions { pub fn default() -> ICU4XFixedDecimalFormatOptions { ICU4XFixedDecimalFormatOptions { - grouping_strategy: 0, - sign_display: 0, + grouping_strategy: ICU4XFixedDecimalGroupingStrategy::Auto, + sign_display: ICU4XFixedDecimalSignDisplay::Auto, } } } @@ -52,19 +67,17 @@ pub mod ffi { provider, FixedDecimalFormatOptions { grouping_strategy: match options.grouping_strategy { - 0 => GroupingStrategy::Auto, - 1 => GroupingStrategy::Never, - 2 => GroupingStrategy::Always, - 3 => GroupingStrategy::Min2, - _ => panic!(), + ICU4XFixedDecimalGroupingStrategy::Auto => GroupingStrategy::Auto, + ICU4XFixedDecimalGroupingStrategy::Never => GroupingStrategy::Never, + ICU4XFixedDecimalGroupingStrategy::Always => GroupingStrategy::Always, + ICU4XFixedDecimalGroupingStrategy::Min2 => GroupingStrategy::Min2, }, sign_display: match options.sign_display { - 0 => SignDisplay::Auto, - 1 => SignDisplay::Never, - 2 => SignDisplay::Always, - 3 => SignDisplay::ExceptZero, - 4 => SignDisplay::Negative, - _ => panic!(), + ICU4XFixedDecimalSignDisplay::Auto => SignDisplay::Auto, + ICU4XFixedDecimalSignDisplay::Never => SignDisplay::Never, + ICU4XFixedDecimalSignDisplay::Always => SignDisplay::Always, + ICU4XFixedDecimalSignDisplay::ExceptZero => SignDisplay::ExceptZero, + ICU4XFixedDecimalSignDisplay::Negative => SignDisplay::Negative, }, }, ) { diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 3228ee660..5d29f877d 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -172,8 +172,8 @@ fn gen_bridge(input: ItemMod) -> ItemMod { let module = ast::Module::from_syn(&input, true); let (brace, mut new_contents) = input.content.unwrap(); - new_contents.iter_mut().for_each(|c| { - if let Item::Struct(s) = c { + new_contents.iter_mut().for_each(|c| match c { + Item::Struct(s) => { if !s.attrs.iter().any(|a| { let string_path = a.path.to_token_stream().to_string(); string_path == "repr" || string_path == "diplomat :: opaque" @@ -185,6 +185,16 @@ fn gen_bridge(input: ItemMod) -> ItemMod { .unwrap(); } } + + Item::Enum(e) => { + *e = syn::parse2(quote! { + #[repr(C)] + #e + }) + .unwrap(); + } + + _ => {} }); for custom_type in module.declared_types.values() { @@ -298,4 +308,26 @@ mod tests { .to_string() )); } + + #[test] + fn mod_with_enum() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + enum Abc { + A, + B = 123, + } + + impl Abc { + fn do_something(&self) { + todo!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } } diff --git a/macro/src/snapshots/diplomat__tests__mod_with_enum.snap b/macro/src/snapshots/diplomat__tests__mod_with_enum.snap new file mode 100644 index 000000000..1a14d5ad5 --- /dev/null +++ b/macro/src/snapshots/diplomat__tests__mod_with_enum.snap @@ -0,0 +1,24 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n enum Abc { A, B = 123, } impl Abc\n { fn do_something(& self) { todo ! () } }\n }\n }).to_token_stream().to_string())" + +--- +mod ffi { + #[repr(C)] + enum Abc { + A, + B = 123, + } + impl Abc { + fn do_something(&self) { + todo!() + } + } + #[no_mangle] + extern "C" fn Abc_do_something(this: &Abc) { + this.do_something(); + } + #[no_mangle] + extern "C" fn Abc_destroy(this: Box) {} +} + diff --git a/tool/src/c/mod.rs b/tool/src/c/mod.rs index 133694f5e..dd1c861ca 100644 --- a/tool/src/c/mod.rs +++ b/tool/src/c/mod.rs @@ -31,9 +31,27 @@ pub fn gen_bindings( all_types.sort_by_key(|t| t.1.name()); for (in_path, custom_type) in &all_types { - if let ast::CustomType::Opaque(_) = custom_type { - writeln!(out)?; - gen_struct(custom_type, in_path, env, out)?; + match custom_type { + ast::CustomType::Opaque(_) => { + writeln!(out)?; + gen_struct(custom_type, in_path, env, out)?; + } + + ast::CustomType::Enum(enm) => { + writeln!(out)?; + writeln!(out, "enum {} {{", enm.name)?; + let mut enum_body_out = indented(out).with_str(" "); + for (name, discriminant, _) in enm.variants.iter() { + writeln!( + &mut enum_body_out, + "{}_{} = {},", + enm.name, name, discriminant + )?; + } + writeln!(out, "}};")?; + } + + ast::CustomType::Struct(_) => {} } } @@ -64,12 +82,18 @@ pub fn gen_bindings( gen_method(method, in_path, env, out)?; } - writeln!( + write!(out, "void {}_destroy(", custom_type.name())?; + + gen_type( + &ast::TypeName::Box(Box::new(ast::TypeName::Named( + ast::Path::empty().sub_path(custom_type.name().clone()), + ))), + in_path, + env, out, - "void {}_destroy({}* self);", - custom_type.name(), - custom_type.name() )?; + + writeln!(out, " self);")?; } writeln!(out, "#ifdef __cplusplus")?; @@ -100,6 +124,8 @@ fn gen_struct( writeln!(out)?; writeln!(out, "}} {};", strct.name)?; } + + ast::CustomType::Enum(_) => {} } Ok(()) @@ -169,9 +195,16 @@ fn gen_type( out: &mut W, ) -> fmt::Result { match typ { - ast::TypeName::Named(_) => { - write!(out, "{}", typ.resolve(in_path, env).name())?; - } + ast::TypeName::Named(_) => match typ.resolve(in_path, env) { + r @ ast::CustomType::Struct(_) | r @ ast::CustomType::Opaque(_) => { + write!(out, "{}", r.name())?; + } + + ast::CustomType::Enum(_) => { + // repr(C) fieldless enums use the default platform representation: isize + write!(out, "ssize_t")?; + } + }, ast::TypeName::Box(underlying) => { gen_type(underlying.as_ref(), in_path, env, out)?; @@ -242,7 +275,7 @@ pub fn topological_sort_structs<'a>( topological_sort_structs(strct, path, seen, order, env); } } - (_, ast::CustomType::Opaque(_)) => {} + (_, ast::CustomType::Opaque(_) | ast::CustomType::Enum(_)) => {} } } } diff --git a/tool/src/cpp/mod.rs b/tool/src/cpp/mod.rs index 9cca8e61a..213aa40d3 100644 --- a/tool/src/cpp/mod.rs +++ b/tool/src/cpp/mod.rs @@ -41,6 +41,15 @@ pub fn gen_bindings( writeln!(out, "class {};", custom_type.name())?; } + ast::CustomType::Enum(enm) => { + writeln!(out, "enum struct {} : ssize_t {{", enm.name)?; + let mut enm_indent = indented(out).with_str(" "); + for (name, discriminant, _) in enm.variants.iter() { + writeln!(&mut enm_indent, "{} = {},", name, discriminant)?; + } + writeln!(out, "}};")?; + } + ast::CustomType::Struct(_) => { writeln!(out, "struct {};", custom_type.name())?; } @@ -196,6 +205,8 @@ fn gen_struct( writeln!(out, "}};")?; } } + + ast::CustomType::Enum(_) => {} } Ok(()) @@ -366,6 +377,13 @@ fn gen_type( write!(out, "*")?; } } + + ast::CustomType::Enum(enm) => { + write!(out, "{}", enm.name)?; + if behind_ref { + write!(out, "*")?; + } + } }, ast::TypeName::Box(underlying) => { @@ -443,6 +461,11 @@ fn gen_rust_to_cpp( // TODO(shadaj): should emit a unique_ptr todo!("Receiving boxes of structs is not yet supported") } + + ast::CustomType::Enum(_) => { + // TODO(shadaj): should emit a unique_ptr + todo!("Receiving boxes of enums is not yet supported") + } }, _o => todo!(), }, @@ -472,6 +495,10 @@ fn gen_rust_to_cpp( format!("{}{{ {} }}", strct.name, all_fields_wrapped.join(", ")) } + + (_, ast::CustomType::Enum(enm)) => { + format!("{}{{ {} }}", enm.name, cpp) + } }, ast::TypeName::Option(underlying) => match underlying.as_ref() { @@ -566,6 +593,8 @@ fn gen_cpp_to_rust( all_fields_wrapped.join(", ") ) } + + ast::CustomType::Enum(_) => format!("static_cast({})", cpp), }, ast::TypeName::Writeable => { if behind_ref { diff --git a/tool/src/js/mod.rs b/tool/src/js/mod.rs index 4f25806c2..5195bca00 100644 --- a/tool/src/js/mod.rs +++ b/tool/src/js/mod.rs @@ -54,44 +54,61 @@ fn gen_struct( in_path: &ast::Path, env: &HashMap>, ) -> fmt::Result { - writeln!( - out, - "const {}_box_destroy_registry = new FinalizationRegistry(underlying => {{", - custom_type.name() - )?; - writeln!( - indented(out).with_str(" "), - "wasm.{}_destroy(underlying);", - custom_type.name() - )?; - writeln!(out, "}});")?; - writeln!(out)?; + if let ast::CustomType::Enum(enm) = custom_type { + writeln!(out, "const {}_js_to_rust = {{", enm.name)?; + let mut enm_body_out = indented(out).with_str(" "); + for (name, discriminant, _) in enm.variants.iter() { + writeln!(&mut enm_body_out, "\"{}\": {},", name, discriminant)?; + } + writeln!(out, "}};")?; - writeln!(out, "export class {} {{", custom_type.name())?; + writeln!(out, "const {}_rust_to_js = {{", enm.name)?; + let mut enm_reverse_body_out = indented(out).with_str(" "); + for (name, discriminant, _) in enm.variants.iter() { + writeln!(&mut enm_reverse_body_out, "{}: \"{}\",", discriminant, name)?; + } + writeln!(out, "}};")?; + } else { + writeln!( + out, + "const {}_box_destroy_registry = new FinalizationRegistry(underlying => {{", + custom_type.name() + )?; + writeln!( + indented(out).with_str(" "), + "wasm.{}_destroy(underlying);", + custom_type.name() + )?; + writeln!(out, "}});")?; + writeln!(out)?; - let mut class_body_out = indented(out).with_str(" "); + writeln!(out, "export class {} {{", custom_type.name())?; - writeln!(&mut class_body_out, "constructor(underlying) {{")?; - writeln!( - indented(&mut class_body_out).with_str(" "), - "this.underlying = underlying;" - )?; - writeln!(&mut class_body_out, "}}")?; + let mut class_body_out = indented(out).with_str(" "); - for method in custom_type.methods().iter() { - writeln!(&mut class_body_out)?; - gen_method(method, in_path, env, &mut class_body_out)?; - } + writeln!(&mut class_body_out, "constructor(underlying) {{")?; + writeln!( + indented(&mut class_body_out).with_str(" "), + "this.underlying = underlying;" + )?; + writeln!(&mut class_body_out, "}}")?; - if let ast::CustomType::Struct(strct) = custom_type { - let (_, offsets, _) = layout::struct_size_offsets_max_align(strct, in_path, env); - for ((name, typ, _), offset) in strct.fields.iter().zip(offsets.iter()) { + for method in custom_type.methods().iter() { writeln!(&mut class_body_out)?; - gen_field(name, typ, in_path, *offset, env, &mut class_body_out)?; + gen_method(method, in_path, env, &mut class_body_out)?; + } + + if let ast::CustomType::Struct(strct) = custom_type { + let (_, offsets, _) = layout::struct_size_offsets_max_align(strct, in_path, env); + for ((name, typ, _), offset) in strct.fields.iter().zip(offsets.iter()) { + writeln!(&mut class_body_out)?; + gen_field(name, typ, in_path, *offset, env, &mut class_body_out)?; + } } + + writeln!(out, "}}")?; } - writeln!(out, "}}")?; Ok(()) } @@ -288,6 +305,10 @@ fn gen_value_js_to_rust( } } + ast::CustomType::Enum(enm) => { + invocation_params.push(format!("{}_js_to_rust[{}]", enm.name, param_name)); + } + ast::CustomType::Opaque(_) => { panic!("Opaque types cannot be sent as values"); } @@ -342,6 +363,13 @@ fn gen_value_rust_to_js( writeln!(&mut iife_indent, "return out;")?; write!(out, "}})()")?; } + + ast::CustomType::Enum(enm) => { + write!(out, "{}_rust_to_js[", enm.name)?; + value_expr(out)?; + write!(out, "]")?; + } + ast::CustomType::Opaque(_) => { panic!("Opaque types cannot be used in value position") } @@ -462,16 +490,29 @@ fn gen_rust_reference_to_js( ast::TypeName::Named(_) => { let custom_type = underlying.resolve(in_path, env); - writeln!(out, "(() => {{")?; - let mut iife_indent = indented(out).with_str(" "); - write!(&mut iife_indent, "const out = new {}(", custom_type.name())?; - value_expr(&mut iife_indent)?; - writeln!(&mut iife_indent, ");")?; - // TODO(shadaj): add lifetime references + if let ast::CustomType::Enum(enm) = custom_type { + write!(out, "{}_rust_to_js[", enm.name)?; + gen_rust_reference_to_js( + &ast::TypeName::Primitive(PrimitiveType::isize), + in_path, + value_expr, + env, + out, + )?; + write!(out, "]")?; + } else { + writeln!(out, "(() => {{")?; + let mut iife_indent = indented(out).with_str(" "); + write!(&mut iife_indent, "const out = new {}(", custom_type.name())?; + value_expr(&mut iife_indent)?; + writeln!(&mut iife_indent, ");")?; + + // TODO(shadaj): add lifetime references - writeln!(&mut iife_indent, "return out;")?; - write!(out, "}})()")?; + writeln!(&mut iife_indent, "return out;")?; + write!(out, "}})()")?; + } } ast::TypeName::Primitive(prim) => { diff --git a/tool/src/layout.rs b/tool/src/layout.rs index bf802ebad..77ea688f3 100644 --- a/tool/src/layout.rs +++ b/tool/src/layout.rs @@ -45,6 +45,11 @@ pub fn type_size_alignment( (size, max_align) } + ast::CustomType::Enum(_) => { + // repr(C) fieldless enums use the default platform representation: isize + (4, 4) + } + ast::CustomType::Opaque(_) => { panic!("Size of opaque types is unknown") }