Skip to content

Commit

Permalink
Improved support for string enums (#4147)
Browse files Browse the repository at this point in the history
  • Loading branch information
RunDevelopment authored Oct 8, 2024
1 parent 2a0022a commit 0be2fe5
Show file tree
Hide file tree
Showing 20 changed files with 268 additions and 114 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
* Added `Debug` implementation to `JsError`.
[#4136](https://github.com/rustwasm/wasm-bindgen/pull/4136)

* Added support for `js_name` and `skip_typescript` attributes for string enums.
[#4147](https://github.com/rustwasm/wasm-bindgen/pull/4147)

### Changed

* Implicitly enable reference type and multivalue transformations if the module already makes use of the corresponding target features.
Expand Down Expand Up @@ -79,6 +82,9 @@
* Specify `"type": "module"` when deploying to nodejs-module
[#4092](https://github.com/rustwasm/wasm-bindgen/pull/4092)

* Fixed string enums not generating TypeScript types.
[#4147](https://github.com/rustwasm/wasm-bindgen/pull/4147)

--------------------------------------------------------------------------------

## [0.2.93](https://github.com/rustwasm/wasm-bindgen/compare/0.2.92...0.2.93)
Expand Down
6 changes: 6 additions & 0 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,18 @@ pub struct StringEnum {
pub vis: syn::Visibility,
/// The Rust enum's identifiers
pub name: Ident,
/// The name of this string enum in JS/TS code
pub js_name: String,
/// The Rust identifiers for the variants
pub variants: Vec<Ident>,
/// The JS string values of the variants
pub variant_values: Vec<String>,
/// The doc comments on this enum, if any
pub comments: Vec<String>,
/// Attributes to apply to the Rust enum
pub rust_attrs: Vec<syn::Attribute>,
/// Whether to generate a typescript definition for this enum
pub generate_typescript: bool,
/// Path to wasm_bindgen
pub wasm_bindgen: Path,
}
Expand Down
12 changes: 1 addition & 11 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ impl ToTokens for ast::StringEnum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let vis = &self.vis;
let enum_name = &self.name;
let name_str = enum_name.to_string();
let name_str = self.js_name.to_string();
let name_len = name_str.len() as u32;
let name_chars = name_str.chars().map(u32::from);
let variants = &self.variants;
Expand Down Expand Up @@ -1172,15 +1172,6 @@ impl ToTokens for ast::StringEnum {

let wasm_bindgen = &self.wasm_bindgen;

let describe_variants = self.variant_values.iter().map(|variant_value| {
let length = variant_value.len() as u32;
let chars = variant_value.chars().map(u32::from);
quote! {
inform(#length);
#(inform(#chars);)*
}
});

(quote! {
#(#attrs)*
#[non_exhaustive]
Expand Down Expand Up @@ -1257,7 +1248,6 @@ impl ToTokens for ast::StringEnum {
inform(#name_len);
#(inform(#name_chars);)*
inform(#variant_count);
#(#describe_variants)*
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/backend/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,14 @@ fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> Impor
}
}

fn shared_import_enum<'a>(_i: &'a ast::StringEnum, _intern: &'a Interner) -> StringEnum {
StringEnum {}
fn shared_import_enum<'a>(i: &'a ast::StringEnum, _intern: &'a Interner) -> StringEnum<'a> {
StringEnum {
name: &i.js_name,
public: matches!(i.vis, syn::Visibility::Public(_)),
generate_typescript: i.generate_typescript,
variant_values: i.variant_values.iter().map(|x| &**x).collect(),
comments: i.comments.iter().map(|s| &**s).collect(),
}
}

fn shared_struct<'a>(s: &'a ast::Struct, intern: &'a Interner) -> Struct<'a> {
Expand Down
3 changes: 0 additions & 3 deletions crates/cli-support/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ pub enum Descriptor {
name: String,
invalid: u32,
hole: u32,
variant_values: Vec<String>,
},
RustStruct(String),
Char,
Expand Down Expand Up @@ -171,12 +170,10 @@ impl Descriptor {
let variant_count = get(data);
let invalid = variant_count;
let hole = variant_count + 1;
let variant_values = (0..variant_count).map(|_| get_string(data)).collect();
Descriptor::StringEnum {
name,
invalid,
hole,
variant_values,
}
}
RUST_STRUCT => {
Expand Down
46 changes: 14 additions & 32 deletions crates/cli-support/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,20 +576,11 @@ fn instruction(
log_error: &mut bool,
constructor: &Option<String>,
) -> Result<(), Error> {
fn wasm_to_string_enum(variant_values: &[String], index: &str) -> String {
fn wasm_to_string_enum(name: &str, index: &str) -> String {
// e.g. ["a","b","c"][someIndex]
let mut enum_val_expr = String::new();
enum_val_expr.push('[');
for variant in variant_values {
enum_val_expr.push_str(&format!("\"{variant}\","));
}
enum_val_expr.push(']');
enum_val_expr.push('[');
enum_val_expr.push_str(index);
enum_val_expr.push(']');
enum_val_expr
format!("__wbindgen_enum_{name}[{index}]")
}
fn string_enum_to_wasm(variant_values: &[String], invalid: u32, enum_val: &str) -> String {
fn string_enum_to_wasm(name: &str, invalid: u32, enum_val: &str) -> String {
// e.g. (["a","b","c"].indexOf(someEnumVal) + 1 || 4) - 1
// |
// invalid + 1
Expand All @@ -598,16 +589,10 @@ fn instruction(
// and with +1 we get 0 which is falsey, so we can use || to
// substitute invalid+1. Finally, we just do -1 to get the correct
// values for everything.
let mut enum_val_expr = String::new();
enum_val_expr.push_str("([");
for variant in variant_values.iter() {
enum_val_expr.push_str(&format!("\"{variant}\","));
}
enum_val_expr.push_str(&format!(
"].indexOf({enum_val}) + 1 || {invalid}) - 1",
format!(
"(__wbindgen_enum_{name}.indexOf({enum_val}) + 1 || {invalid}) - 1",
invalid = invalid + 1
));
enum_val_expr
)
}

match instr {
Expand Down Expand Up @@ -702,34 +687,31 @@ fn instruction(
}
}

Instruction::WasmToStringEnum { variant_values } => {
Instruction::WasmToStringEnum { name } => {
let index = js.pop();
js.push(wasm_to_string_enum(variant_values, &index))
js.push(wasm_to_string_enum(name, &index))
}

Instruction::OptionWasmToStringEnum { variant_values, .. } => {
Instruction::OptionWasmToStringEnum { name, .. } => {
// Since hole is currently variant_count+1 and the lookup is
// ["a","b","c"][index], the lookup will implicitly return map
// the hole to undefined, because OOB indexes will return undefined.
let index = js.pop();
js.push(wasm_to_string_enum(variant_values, &index))
js.push(wasm_to_string_enum(name, &index))
}

Instruction::StringEnumToWasm {
variant_values,
invalid,
} => {
Instruction::StringEnumToWasm { name, invalid } => {
let enum_val = js.pop();
js.push(string_enum_to_wasm(variant_values, *invalid, &enum_val))
js.push(string_enum_to_wasm(name, *invalid, &enum_val))
}

Instruction::OptionStringEnumToWasm {
variant_values,
name,
invalid,
hole,
} => {
let enum_val = js.pop();
let enum_val_expr = string_enum_to_wasm(variant_values, *invalid, &enum_val);
let enum_val_expr = string_enum_to_wasm(name, *invalid, &enum_val);

// e.g. someEnumVal == undefined ? 4 : (string_enum_to_wasm(someEnumVal))
// |
Expand Down
43 changes: 41 additions & 2 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::descriptor::VectorKind;
use crate::intrinsic::Intrinsic;
use crate::wit::{
Adapter, AdapterId, AdapterJsImportKind, AdapterType, AuxExportedMethodKind, AuxReceiverKind,
AuxValue,
AuxStringEnum, AuxValue,
};
use crate::wit::{AdapterKind, Instruction, InstructionData};
use crate::wit::{AuxEnum, AuxExport, AuxExportKind, AuxImport, AuxStruct};
Expand Down Expand Up @@ -2529,9 +2529,12 @@ __wbg_set_wasm(wasm);"
pairs.sort_by_key(|(k, _)| *k);
check_duplicated_getter_and_setter_names(&pairs)?;

for (_, e) in self.aux.enums.iter() {
for (_, e) in crate::sorted_iter(&self.aux.enums) {
self.generate_enum(e)?;
}
for (_, e) in crate::sorted_iter(&self.aux.string_enums) {
self.generate_string_enum(e)?;
}

for s in self.aux.structs.iter() {
self.generate_struct(s)?;
Expand Down Expand Up @@ -3808,6 +3811,42 @@ __wbg_set_wasm(wasm);"
Ok(())
}

fn generate_string_enum(&mut self, string_enum: &AuxStringEnum) -> Result<(), Error> {
let docs = format_doc_comments(&string_enum.comments, None);

let variants: Vec<_> = string_enum
.variant_values
.iter()
.map(|v| format!("\"{v}\""))
.collect();

if string_enum.generate_typescript {
self.typescript.push_str(&docs);
if string_enum.public {
self.typescript.push_str("export ");
}
self.typescript.push_str("type ");
self.typescript.push_str(&string_enum.name);
self.typescript.push_str(" = ");

if variants.is_empty() {
self.typescript.push_str("never");
} else {
self.typescript.push_str(&variants.join(" | "));
}

self.typescript.push_str(";\n");
}

self.global(&format!(
"const __wbindgen_enum_{name} = [{values}];\n",
name = string_enum.name,
values = variants.join(", ")
));

Ok(())
}

fn generate_struct(&mut self, struct_: &AuxStruct) -> Result<(), Error> {
let class = require_class(&mut self.exported_classes, &struct_.name);
class.comments = format_doc_comments(&struct_.comments, None);
Expand Down
7 changes: 3 additions & 4 deletions crates/cli-support/src/wit/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::I32],
);
},
Descriptor::StringEnum { name, variant_values, invalid, .. } => {
Descriptor::StringEnum { name, invalid, .. } => {
self.instruction(
&[AdapterType::StringEnum(name.clone())],
Instruction::StringEnumToWasm {
variant_values: variant_values.clone(),
name: name.clone(),
invalid: *invalid,
},
&[AdapterType::I32],
Expand Down Expand Up @@ -308,14 +308,13 @@ impl InstructionBuilder<'_, '_> {
}
Descriptor::StringEnum {
name,
variant_values,
invalid,
hole,
} => {
self.instruction(
&[AdapterType::StringEnum(name.clone()).option()],
Instruction::OptionStringEnumToWasm {
variant_values: variant_values.clone(),
name: name.clone(),
invalid: *invalid,
hole: *hole,
},
Expand Down
29 changes: 28 additions & 1 deletion crates/cli-support/src/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ impl<'a> Context<'a> {
decode::ImportKind::Static(s) => self.import_static(&import, s),
decode::ImportKind::String(s) => self.import_string(s),
decode::ImportKind::Type(t) => self.import_type(&import, t),
decode::ImportKind::Enum(_) => Ok(()),
decode::ImportKind::Enum(e) => self.string_enum(e),
}
}

Expand Down Expand Up @@ -865,6 +865,33 @@ impl<'a> Context<'a> {
Ok(())
}

fn string_enum(&mut self, string_enum: &decode::StringEnum<'_>) -> Result<(), Error> {
let aux = AuxStringEnum {
name: string_enum.name.to_string(),
public: string_enum.public,
comments: concatenate_comments(&string_enum.comments),
variant_values: string_enum
.variant_values
.iter()
.map(|v| v.to_string())
.collect(),
generate_typescript: string_enum.generate_typescript,
};
let mut result = Ok(());
self.aux
.string_enums
.entry(aux.name.clone())
.and_modify(|existing| {
result = Err(anyhow!(
"duplicate string enums:\n{:?}\n{:?}",
existing,
aux
));
})
.or_insert(aux);
result
}

fn enum_(&mut self, enum_: decode::Enum<'_>) -> Result<(), Error> {
let aux = AuxEnum {
name: enum_.name.to_string(),
Expand Down
17 changes: 17 additions & 0 deletions crates/cli-support/src/wit/nonstandard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub struct WasmBindgenAux {
/// Auxiliary information to go into JS/TypeScript bindings describing the
/// exported enums from Rust.
pub enums: HashMap<String, AuxEnum>,
/// Auxiliary information to go into JS/TypeScript bindings describing the
/// exported string enums from Rust.
pub string_enums: HashMap<String, AuxStringEnum>,

/// Auxiliary information to go into JS/TypeScript bindings describing the
/// exported structs from Rust and their fields they've got exported.
Expand Down Expand Up @@ -172,6 +175,20 @@ pub struct AuxEnum {
pub generate_typescript: bool,
}

#[derive(Debug)]
pub struct AuxStringEnum {
/// The name of this enum
pub name: String,
/// Whether this enum is public
pub public: bool,
/// The copied Rust comments to forward to JS
pub comments: String,
/// A list of variants values
pub variant_values: Vec<String>,
/// Whether typescript bindings should be generated for this enum.
pub generate_typescript: bool,
}

#[derive(Debug)]
pub struct AuxStruct {
/// The name of this struct
Expand Down
Loading

0 comments on commit 0be2fe5

Please sign in to comment.