From 6e098156fedf7bacdc0b085f64c69714fee266e9 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 7 Dec 2024 21:30:49 +0100 Subject: [PATCH 1/7] Fixed webidl output formatting on Windows (#4335) --- crates/web-sys/README.md | 3 --- crates/webidl/src/lib.rs | 23 +++++++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/web-sys/README.md b/crates/web-sys/README.md index 7693680c6fe..53753673564 100644 --- a/crates/web-sys/README.md +++ b/crates/web-sys/README.md @@ -32,9 +32,6 @@ If you don't see a particular web API in `web-sys`, here is how to add it. 2. Annotate the functions that can throw with `[Throws]` 3. `cd crates/web-sys` 4. Run `cargo run --release --package wasm-bindgen-webidl -- webidls src/features ./Cargo.toml` - - If formatting fails, you can run `cargo fmt` in the `crates/web-sys` directory. On Windows, you might also want to run `cargo fmt -- --config newline_style=Unix` depending on your git configuration. - 5. Run `git add .` to add all the generated files into git. 6. Add an entry in CHANGELOG.md like the following diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index a6caa7d5145..778b1f40d71 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -997,14 +997,21 @@ pub fn generate(from: &Path, to: &Path, options: Options) -> Result { fn rustfmt(paths: impl IntoIterator) -> Result<()> { // run rustfmt on the generated file - really handy for debugging - let result = Command::new("rustfmt") - .arg("--edition") - .arg("2021") - .args(paths) - .status() - .context("rustfmt failed")?; - - assert!(result.success(), "rustfmt failed"); + + // On Windows, the command line length is limited to 32k characters, so + // we need to split the command into multiple invocations. I've + // arbitrarily chosen to format 400 files at a time, because it works. + let paths: Vec<_> = paths.into_iter().collect(); + for chunk in paths.chunks(400) { + let result = Command::new("rustfmt") + .arg("--edition") + .arg("2021") + .args(chunk) + .status() + .context("rustfmt failed")?; + + assert!(result.success(), "rustfmt failed"); + } Ok(()) } From 720b9fe6315a7783c999375e0f7714ebd55a0b40 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 7 Dec 2024 21:44:39 +0100 Subject: [PATCH 2/7] Make API for exporting JS content more explicit (#4333) --- crates/cli-support/src/js/mod.rs | 76 ++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index e26f7a6d2ca..9cdd2d7ddb0 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -120,6 +120,18 @@ struct FieldAccessor { is_optional: bool, } +/// Different JS constructs that can be exported. +enum ExportJs<'a> { + /// A class of the form `class Name {...}`. + Class(&'a str), + /// An anonymous function expression of the form `function(...) {...}`. + /// + /// Note that the function name is not included in the string. + Function(&'a str), + /// An arbitrary JS expression. + Expression(&'a str), +} + const INITIAL_HEAP_VALUES: &[&str] = &["undefined", "null", "true", "false"]; // Must be kept in sync with `src/lib.rs` of the `wasm-bindgen` crate const INITIAL_HEAP_OFFSET: usize = 128; @@ -163,38 +175,46 @@ impl<'a> Context<'a> { fn export( &mut self, export_name: &str, - contents: &str, + export: ExportJs, comments: Option<&str>, ) -> Result<(), Error> { let definition_name = self.generate_identifier(export_name); - if contents.starts_with("class") && definition_name != export_name { + if matches!(export, ExportJs::Class(_)) && definition_name != export_name { bail!("cannot shadow already defined class `{}`", export_name); } - let contents = contents.trim(); + // write out comments if let Some(c) = comments { self.globals.push_str(c); } + let global = match self.config.mode { - OutputMode::Node { module: false } => { - if contents.starts_with("class") { - format!("{}\nmodule.exports.{1} = {1};\n", contents, export_name) - } else { - format!("module.exports.{} = {};\n", export_name, contents) + OutputMode::Node { module: false } => match export { + ExportJs::Class(class) => { + format!("{}\nmodule.exports.{1} = {1};\n", class, export_name) } - } - OutputMode::NoModules { .. } => { - if contents.starts_with("class") { - format!("{}\n__exports.{1} = {1};\n", contents, export_name) - } else { - format!("__exports.{} = {};\n", export_name, contents) + ExportJs::Function(expr) | ExportJs::Expression(expr) => { + format!("module.exports.{} = {};\n", export_name, expr) } - } + }, + OutputMode::NoModules { .. } => match export { + ExportJs::Class(class) => { + format!("{}\n__exports.{1} = {1};\n", class, export_name) + } + ExportJs::Function(expr) | ExportJs::Expression(expr) => { + format!("__exports.{} = {};\n", export_name, expr) + } + }, OutputMode::Bundler { .. } | OutputMode::Node { module: true } | OutputMode::Web - | OutputMode::Deno => { - if let Some(body) = contents.strip_prefix("function") { + | OutputMode::Deno => match export { + ExportJs::Class(class) => { + assert_eq!(export_name, definition_name); + format!("export {}\n", class) + } + ExportJs::Function(function) => { + let body = function.strip_prefix("function").unwrap(); if export_name == definition_name { format!("export function {}{}\n", export_name, body) } else { @@ -203,14 +223,12 @@ impl<'a> Context<'a> { definition_name, body, definition_name, export_name, ) } - } else if contents.starts_with("class") { - assert_eq!(export_name, definition_name); - format!("export {}\n", contents) - } else { + } + ExportJs::Expression(expr) => { assert_eq!(export_name, definition_name); - format!("export const {} = {};\n", export_name, contents) + format!("export const {} = {};\n", export_name, expr) } - } + }, }; self.global(&global); Ok(()) @@ -1169,10 +1187,10 @@ __wbg_set_wasm(wasm);" self.write_class_field_types(class, &mut ts_dst); - dst.push_str("}\n"); + dst.push('}'); ts_dst.push_str("}\n"); - self.export(name, &dst, Some(&class.comments))?; + self.export(name, ExportJs::Class(&dst), Some(&class.comments))?; if class.generate_typescript { self.typescript.push_str(&class.comments); @@ -2872,7 +2890,11 @@ __wbg_set_wasm(wasm);" self.typescript.push_str(";\n"); } - self.export(name, &format!("function{}", code), Some(&js_docs))?; + self.export( + name, + ExportJs::Function(&format!("function{}", code)), + Some(&js_docs), + )?; self.globals.push('\n'); } AuxExportKind::Constructor(class) => { @@ -3995,7 +4017,7 @@ __wbg_set_wasm(wasm);" self.export( &enum_.name, - &format!("Object.freeze({{\n{}}})", variants), + ExportJs::Expression(&format!("Object.freeze({{\n{}}})", variants)), Some(&docs), )?; From f93111212e1b13f903d5d3114f219897fe87644e Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 7 Dec 2024 22:20:27 +0100 Subject: [PATCH 3/7] Add `null` to argument types of optional parameters (#4188) --- CHANGELOG.md | 9 +++ crates/cli-support/src/js/binding.rs | 43 ++++++++--- crates/cli/tests/reference/echo.d.ts | 72 +++++++++---------- crates/cli/tests/reference/echo.js | 72 +++++++++---------- crates/cli/tests/reference/enums.d.ts | 6 +- crates/cli/tests/reference/enums.js | 6 +- crates/cli/tests/reference/getter-setter.d.ts | 13 ++-- crates/cli/tests/reference/getter-setter.js | 10 +-- crates/cli/tests/reference/int128.d.ts | 4 +- crates/cli/tests/reference/int128.js | 4 +- crates/cli/tests/reference/optional-args.d.ts | 4 ++ crates/cli/tests/reference/optional-args.js | 38 ++++++++++ crates/cli/tests/reference/optional-args.rs | 7 ++ crates/cli/tests/reference/optional-args.wat | 17 +++++ .../typescript-tests/src/optional_fields.ts | 9 ++- 15 files changed, 211 insertions(+), 103 deletions(-) create mode 100644 crates/cli/tests/reference/optional-args.d.ts create mode 100644 crates/cli/tests/reference/optional-args.js create mode 100644 crates/cli/tests/reference/optional-args.rs create mode 100644 crates/cli/tests/reference/optional-args.wat diff --git a/CHANGELOG.md b/CHANGELOG.md index 32405a47e32..3402f7ae975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # `wasm-bindgen` Change Log -------------------------------------------------------------------------------- +## Unreleased + +### Changed + +* Optional parameters are now typed as `T | undefined | null` to reflect the actual JS behavior. + [#4188](https://github.com/rustwasm/wasm-bindgen/pull/4188) + +-------------------------------------------------------------------------------- + ## [0.2.99](https://github.com/rustwasm/wasm-bindgen/compare/0.2.98...0.2.99) Released 2024-12-07 diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 54be1481163..637c3a5edf7 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -321,13 +321,15 @@ impl<'a, 'b> Builder<'a, 'b> { let mut ts = String::new(); match ty { AdapterType::Option(ty) if omittable => { + // e.g. `foo?: string | null` arg.push_str("?: "); - adapter2ts(ty, &mut ts, Some(&mut ts_refs)); + adapter2ts(ty, TypePosition::Argument, &mut ts, Some(&mut ts_refs)); + ts.push_str(" | null"); } ty => { omittable = false; arg.push_str(": "); - adapter2ts(ty, &mut ts, Some(&mut ts_refs)); + adapter2ts(ty, TypePosition::Argument, &mut ts, Some(&mut ts_refs)); } } arg.push_str(&ts); @@ -363,7 +365,12 @@ impl<'a, 'b> Builder<'a, 'b> { let mut ret = String::new(); match result_tys.len() { 0 => ret.push_str("void"), - 1 => adapter2ts(&result_tys[0], &mut ret, Some(&mut ts_refs)), + 1 => adapter2ts( + &result_tys[0], + TypePosition::Return, + &mut ret, + Some(&mut ts_refs), + ), _ => ret.push_str("[any]"), } if asyncness { @@ -395,16 +402,18 @@ impl<'a, 'b> Builder<'a, 'b> { for (name, ty) in fn_arg_names.iter().zip(arg_tys).rev() { let mut arg = "@param {".to_string(); - adapter2ts(ty, &mut arg, None); - arg.push_str("} "); match ty { - AdapterType::Option(..) if omittable => { + AdapterType::Option(ty) if omittable => { + adapter2ts(ty, TypePosition::Argument, &mut arg, None); + arg.push_str(" | null} "); arg.push('['); arg.push_str(name); arg.push(']'); } _ => { omittable = false; + adapter2ts(ty, TypePosition::Argument, &mut arg, None); + arg.push_str("} "); arg.push_str(name); } } @@ -416,7 +425,7 @@ impl<'a, 'b> Builder<'a, 'b> { if let (Some(name), Some(ty)) = (variadic_arg, arg_tys.last()) { ret.push_str("@param {..."); - adapter2ts(ty, &mut ret, None); + adapter2ts(ty, TypePosition::Argument, &mut ret, None); ret.push_str("} "); ret.push_str(name); ret.push('\n'); @@ -1542,7 +1551,18 @@ impl Invocation { } } -fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet>) { +#[derive(Debug, Clone, Copy)] +enum TypePosition { + Argument, + Return, +} + +fn adapter2ts( + ty: &AdapterType, + position: TypePosition, + dst: &mut String, + refs: Option<&mut HashSet>, +) { match ty { AdapterType::I32 | AdapterType::S8 @@ -1564,8 +1584,11 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet dst.push_str("boolean"), AdapterType::Vector(kind) => dst.push_str(&kind.js_ty()), AdapterType::Option(ty) => { - adapter2ts(ty, dst, refs); - dst.push_str(" | undefined"); + adapter2ts(ty, position, dst, refs); + dst.push_str(match position { + TypePosition::Argument => " | null | undefined", + TypePosition::Return => " | undefined", + }); } AdapterType::NamedExternref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), diff --git a/crates/cli/tests/reference/echo.d.ts b/crates/cli/tests/reference/echo.d.ts index c318f636c11..61fd4a7925e 100644 --- a/crates/cli/tests/reference/echo.d.ts +++ b/crates/cli/tests/reference/echo.d.ts @@ -36,42 +36,42 @@ export function echo_vec_uninit_i64(a: BigInt64Array): BigInt64Array; export function echo_vec_string(a: (string)[]): (string)[]; export function echo_struct(a: Foo): Foo; export function echo_vec_struct(a: (Foo)[]): (Foo)[]; -export function echo_option_u8(a?: number): number | undefined; -export function echo_option_i8(a?: number): number | undefined; -export function echo_option_u16(a?: number): number | undefined; -export function echo_option_i16(a?: number): number | undefined; -export function echo_option_u32(a?: number): number | undefined; -export function echo_option_i32(a?: number): number | undefined; -export function echo_option_u64(a?: bigint): bigint | undefined; -export function echo_option_i64(a?: bigint): bigint | undefined; -export function echo_option_u128(a?: bigint): bigint | undefined; -export function echo_option_i128(a?: bigint): bigint | undefined; -export function echo_option_usize(a?: number): number | undefined; -export function echo_option_isize(a?: number): number | undefined; -export function echo_option_f32(a?: number): number | undefined; -export function echo_option_f64(a?: number): number | undefined; -export function echo_option_bool(a?: boolean): boolean | undefined; -export function echo_option_char(a?: string): string | undefined; -export function echo_option_string(a?: string): string | undefined; -export function echo_option_vec_u8(a?: Uint8Array): Uint8Array | undefined; -export function echo_option_vec_i8(a?: Int8Array): Int8Array | undefined; -export function echo_option_vec_u16(a?: Uint16Array): Uint16Array | undefined; -export function echo_option_vec_i16(a?: Int16Array): Int16Array | undefined; -export function echo_option_vec_u32(a?: Uint32Array): Uint32Array | undefined; -export function echo_option_vec_i32(a?: Int32Array): Int32Array | undefined; -export function echo_option_vec_u64(a?: BigUint64Array): BigUint64Array | undefined; -export function echo_option_vec_i64(a?: BigInt64Array): BigInt64Array | undefined; -export function echo_option_vec_uninit_u8(a?: Uint8Array): Uint8Array | undefined; -export function echo_option_vec_uninit_i8(a?: Int8Array): Int8Array | undefined; -export function echo_option_vec_uninit_u16(a?: Uint16Array): Uint16Array | undefined; -export function echo_option_vec_uninit_i16(a?: Int16Array): Int16Array | undefined; -export function echo_option_vec_uninit_u32(a?: Uint32Array): Uint32Array | undefined; -export function echo_option_vec_uninit_i32(a?: Int32Array): Int32Array | undefined; -export function echo_option_vec_uninit_u64(a?: BigUint64Array): BigUint64Array | undefined; -export function echo_option_vec_uninit_i64(a?: BigInt64Array): BigInt64Array | undefined; -export function echo_option_vec_string(a?: (string)[]): (string)[] | undefined; -export function echo_option_struct(a?: Foo): Foo | undefined; -export function echo_option_vec_struct(a?: (Foo)[]): (Foo)[] | undefined; +export function echo_option_u8(a?: number | null): number | undefined; +export function echo_option_i8(a?: number | null): number | undefined; +export function echo_option_u16(a?: number | null): number | undefined; +export function echo_option_i16(a?: number | null): number | undefined; +export function echo_option_u32(a?: number | null): number | undefined; +export function echo_option_i32(a?: number | null): number | undefined; +export function echo_option_u64(a?: bigint | null): bigint | undefined; +export function echo_option_i64(a?: bigint | null): bigint | undefined; +export function echo_option_u128(a?: bigint | null): bigint | undefined; +export function echo_option_i128(a?: bigint | null): bigint | undefined; +export function echo_option_usize(a?: number | null): number | undefined; +export function echo_option_isize(a?: number | null): number | undefined; +export function echo_option_f32(a?: number | null): number | undefined; +export function echo_option_f64(a?: number | null): number | undefined; +export function echo_option_bool(a?: boolean | null): boolean | undefined; +export function echo_option_char(a?: string | null): string | undefined; +export function echo_option_string(a?: string | null): string | undefined; +export function echo_option_vec_u8(a?: Uint8Array | null): Uint8Array | undefined; +export function echo_option_vec_i8(a?: Int8Array | null): Int8Array | undefined; +export function echo_option_vec_u16(a?: Uint16Array | null): Uint16Array | undefined; +export function echo_option_vec_i16(a?: Int16Array | null): Int16Array | undefined; +export function echo_option_vec_u32(a?: Uint32Array | null): Uint32Array | undefined; +export function echo_option_vec_i32(a?: Int32Array | null): Int32Array | undefined; +export function echo_option_vec_u64(a?: BigUint64Array | null): BigUint64Array | undefined; +export function echo_option_vec_i64(a?: BigInt64Array | null): BigInt64Array | undefined; +export function echo_option_vec_uninit_u8(a?: Uint8Array | null): Uint8Array | undefined; +export function echo_option_vec_uninit_i8(a?: Int8Array | null): Int8Array | undefined; +export function echo_option_vec_uninit_u16(a?: Uint16Array | null): Uint16Array | undefined; +export function echo_option_vec_uninit_i16(a?: Int16Array | null): Int16Array | undefined; +export function echo_option_vec_uninit_u32(a?: Uint32Array | null): Uint32Array | undefined; +export function echo_option_vec_uninit_i32(a?: Int32Array | null): Int32Array | undefined; +export function echo_option_vec_uninit_u64(a?: BigUint64Array | null): BigUint64Array | undefined; +export function echo_option_vec_uninit_i64(a?: BigInt64Array | null): BigInt64Array | undefined; +export function echo_option_vec_string(a?: (string)[] | null): (string)[] | undefined; +export function echo_option_struct(a?: Foo | null): Foo | undefined; +export function echo_option_vec_struct(a?: (Foo)[] | null): (Foo)[] | undefined; export class Foo { private constructor(); free(): void; diff --git a/crates/cli/tests/reference/echo.js b/crates/cli/tests/reference/echo.js index 1809b0e8618..c3e9c47f835 100644 --- a/crates/cli/tests/reference/echo.js +++ b/crates/cli/tests/reference/echo.js @@ -727,7 +727,7 @@ export function echo_vec_struct(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_u8(a) { @@ -736,7 +736,7 @@ export function echo_option_u8(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_i8(a) { @@ -745,7 +745,7 @@ export function echo_option_i8(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_u16(a) { @@ -754,7 +754,7 @@ export function echo_option_u16(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_i16(a) { @@ -763,7 +763,7 @@ export function echo_option_i16(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_u32(a) { @@ -772,7 +772,7 @@ export function echo_option_u32(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_i32(a) { @@ -781,7 +781,7 @@ export function echo_option_i32(a) { } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_u64(a) { @@ -790,7 +790,7 @@ export function echo_option_u64(a) { } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_i64(a) { @@ -799,7 +799,7 @@ export function echo_option_i64(a) { } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_u128(a) { @@ -808,7 +808,7 @@ export function echo_option_u128(a) { } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_i128(a) { @@ -817,7 +817,7 @@ export function echo_option_i128(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_usize(a) { @@ -826,7 +826,7 @@ export function echo_option_usize(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_isize(a) { @@ -835,7 +835,7 @@ export function echo_option_isize(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_f32(a) { @@ -844,7 +844,7 @@ export function echo_option_f32(a) { } /** - * @param {number | undefined} [a] + * @param {number | null} [a] * @returns {number | undefined} */ export function echo_option_f64(a) { @@ -853,7 +853,7 @@ export function echo_option_f64(a) { } /** - * @param {boolean | undefined} [a] + * @param {boolean | null} [a] * @returns {boolean | undefined} */ export function echo_option_bool(a) { @@ -862,7 +862,7 @@ export function echo_option_bool(a) { } /** - * @param {string | undefined} [a] + * @param {string | null} [a] * @returns {string | undefined} */ export function echo_option_char(a) { @@ -873,7 +873,7 @@ export function echo_option_char(a) { } /** - * @param {string | undefined} [a] + * @param {string | null} [a] * @returns {string | undefined} */ export function echo_option_string(a) { @@ -889,7 +889,7 @@ export function echo_option_string(a) { } /** - * @param {Uint8Array | undefined} [a] + * @param {Uint8Array | null} [a] * @returns {Uint8Array | undefined} */ export function echo_option_vec_u8(a) { @@ -905,7 +905,7 @@ export function echo_option_vec_u8(a) { } /** - * @param {Int8Array | undefined} [a] + * @param {Int8Array | null} [a] * @returns {Int8Array | undefined} */ export function echo_option_vec_i8(a) { @@ -921,7 +921,7 @@ export function echo_option_vec_i8(a) { } /** - * @param {Uint16Array | undefined} [a] + * @param {Uint16Array | null} [a] * @returns {Uint16Array | undefined} */ export function echo_option_vec_u16(a) { @@ -937,7 +937,7 @@ export function echo_option_vec_u16(a) { } /** - * @param {Int16Array | undefined} [a] + * @param {Int16Array | null} [a] * @returns {Int16Array | undefined} */ export function echo_option_vec_i16(a) { @@ -953,7 +953,7 @@ export function echo_option_vec_i16(a) { } /** - * @param {Uint32Array | undefined} [a] + * @param {Uint32Array | null} [a] * @returns {Uint32Array | undefined} */ export function echo_option_vec_u32(a) { @@ -969,7 +969,7 @@ export function echo_option_vec_u32(a) { } /** - * @param {Int32Array | undefined} [a] + * @param {Int32Array | null} [a] * @returns {Int32Array | undefined} */ export function echo_option_vec_i32(a) { @@ -985,7 +985,7 @@ export function echo_option_vec_i32(a) { } /** - * @param {BigUint64Array | undefined} [a] + * @param {BigUint64Array | null} [a] * @returns {BigUint64Array | undefined} */ export function echo_option_vec_u64(a) { @@ -1001,7 +1001,7 @@ export function echo_option_vec_u64(a) { } /** - * @param {BigInt64Array | undefined} [a] + * @param {BigInt64Array | null} [a] * @returns {BigInt64Array | undefined} */ export function echo_option_vec_i64(a) { @@ -1017,7 +1017,7 @@ export function echo_option_vec_i64(a) { } /** - * @param {Uint8Array | undefined} [a] + * @param {Uint8Array | null} [a] * @returns {Uint8Array | undefined} */ export function echo_option_vec_uninit_u8(a) { @@ -1033,7 +1033,7 @@ export function echo_option_vec_uninit_u8(a) { } /** - * @param {Int8Array | undefined} [a] + * @param {Int8Array | null} [a] * @returns {Int8Array | undefined} */ export function echo_option_vec_uninit_i8(a) { @@ -1049,7 +1049,7 @@ export function echo_option_vec_uninit_i8(a) { } /** - * @param {Uint16Array | undefined} [a] + * @param {Uint16Array | null} [a] * @returns {Uint16Array | undefined} */ export function echo_option_vec_uninit_u16(a) { @@ -1065,7 +1065,7 @@ export function echo_option_vec_uninit_u16(a) { } /** - * @param {Int16Array | undefined} [a] + * @param {Int16Array | null} [a] * @returns {Int16Array | undefined} */ export function echo_option_vec_uninit_i16(a) { @@ -1081,7 +1081,7 @@ export function echo_option_vec_uninit_i16(a) { } /** - * @param {Uint32Array | undefined} [a] + * @param {Uint32Array | null} [a] * @returns {Uint32Array | undefined} */ export function echo_option_vec_uninit_u32(a) { @@ -1097,7 +1097,7 @@ export function echo_option_vec_uninit_u32(a) { } /** - * @param {Int32Array | undefined} [a] + * @param {Int32Array | null} [a] * @returns {Int32Array | undefined} */ export function echo_option_vec_uninit_i32(a) { @@ -1113,7 +1113,7 @@ export function echo_option_vec_uninit_i32(a) { } /** - * @param {BigUint64Array | undefined} [a] + * @param {BigUint64Array | null} [a] * @returns {BigUint64Array | undefined} */ export function echo_option_vec_uninit_u64(a) { @@ -1129,7 +1129,7 @@ export function echo_option_vec_uninit_u64(a) { } /** - * @param {BigInt64Array | undefined} [a] + * @param {BigInt64Array | null} [a] * @returns {BigInt64Array | undefined} */ export function echo_option_vec_uninit_i64(a) { @@ -1145,7 +1145,7 @@ export function echo_option_vec_uninit_i64(a) { } /** - * @param {(string)[] | undefined} [a] + * @param {(string)[] | null} [a] * @returns {(string)[] | undefined} */ export function echo_option_vec_string(a) { @@ -1161,7 +1161,7 @@ export function echo_option_vec_string(a) { } /** - * @param {Foo | undefined} [a] + * @param {Foo | null} [a] * @returns {Foo | undefined} */ export function echo_option_struct(a) { @@ -1175,7 +1175,7 @@ export function echo_option_struct(a) { } /** - * @param {(Foo)[] | undefined} [a] + * @param {(Foo)[] | null} [a] * @returns {(Foo)[] | undefined} */ export function echo_option_vec_struct(a) { diff --git a/crates/cli/tests/reference/enums.d.ts b/crates/cli/tests/reference/enums.d.ts index da84bbb7740..8d1b955c1f9 100644 --- a/crates/cli/tests/reference/enums.d.ts +++ b/crates/cli/tests/reference/enums.d.ts @@ -1,10 +1,10 @@ /* tslint:disable */ /* eslint-disable */ export function enum_echo(color: Color): Color; -export function option_enum_echo(color?: Color): Color | undefined; +export function option_enum_echo(color?: Color | null): Color | undefined; export function get_name(color: Color): ColorName; -export function option_string_enum_echo(color?: ColorName): ColorName | undefined; -export function option_order(order?: Ordering): Ordering | undefined; +export function option_string_enum_echo(color?: ColorName | null): ColorName | undefined; +export function option_order(order?: Ordering | null): Ordering | undefined; /** * A color. */ diff --git a/crates/cli/tests/reference/enums.js b/crates/cli/tests/reference/enums.js index 13459b9c77e..10d698dc7e5 100644 --- a/crates/cli/tests/reference/enums.js +++ b/crates/cli/tests/reference/enums.js @@ -36,7 +36,7 @@ function isLikeNone(x) { return x === undefined || x === null; } /** - * @param {Color | undefined} [color] + * @param {Color | null} [color] * @returns {Color | undefined} */ export function option_enum_echo(color) { @@ -54,7 +54,7 @@ export function get_name(color) { } /** - * @param {ColorName | undefined} [color] + * @param {ColorName | null} [color] * @returns {ColorName | undefined} */ export function option_string_enum_echo(color) { @@ -63,7 +63,7 @@ export function option_string_enum_echo(color) { } /** - * @param {Ordering | undefined} [order] + * @param {Ordering | null} [order] * @returns {Ordering | undefined} */ export function option_order(order) { diff --git a/crates/cli/tests/reference/getter-setter.d.ts b/crates/cli/tests/reference/getter-setter.d.ts index 8222ca2a462..789fb0f279f 100644 --- a/crates/cli/tests/reference/getter-setter.d.ts +++ b/crates/cli/tests/reference/getter-setter.d.ts @@ -4,10 +4,12 @@ export class Foo { private constructor(); free(): void; x: number; - y?: number; - z?: number; + get y(): number | undefined; + set y(value: number | null | undefined); + get z(): number | undefined; + set z(value: number | null | undefined); readonly lone_getter: number | undefined; - set lone_setter(value: number | undefined); + set lone_setter(value: number | null | undefined); /** * You will only read numbers. */ @@ -17,10 +19,11 @@ export class Foo { * * Yes, this is totally fine in JS. */ - set weird(value: string | undefined); + set weird(value: string | null | undefined); /** * There can be static getters and setters too, and they can even have the * same name as instance getters and setters. */ - static x?: boolean; + static get x(): boolean | undefined; + static set x(value: boolean | null | undefined); } diff --git a/crates/cli/tests/reference/getter-setter.js b/crates/cli/tests/reference/getter-setter.js index fc0ce4820dd..a9df5536a5b 100644 --- a/crates/cli/tests/reference/getter-setter.js +++ b/crates/cli/tests/reference/getter-setter.js @@ -124,7 +124,7 @@ export class Foo { return ret === 0x100000001 ? undefined : ret; } /** - * @param {number | undefined} [arg0] + * @param {number | null} [arg0] */ set y(arg0) { wasm.__wbg_set_foo_y(this.__wbg_ptr, isLikeNone(arg0) ? 0x100000001 : (arg0) >>> 0); @@ -137,7 +137,7 @@ export class Foo { return ret === 0x100000001 ? undefined : ret; } /** - * @param {number | undefined} [z] + * @param {number | null} [z] */ set z(z) { wasm.foo_set_z(this.__wbg_ptr, isLikeNone(z) ? 0x100000001 : (z) >>> 0); @@ -150,7 +150,7 @@ export class Foo { return ret === 0x100000001 ? undefined : ret; } /** - * @param {number | undefined} [value] + * @param {number | null} [value] */ set lone_setter(value) { wasm.foo_set_lone_setter(this.__wbg_ptr, isLikeNone(value) ? 0x100000001 : (value) >>> 0); @@ -167,7 +167,7 @@ export class Foo { * But you must write strings. * * Yes, this is totally fine in JS. - * @param {string | undefined} [value] + * @param {string | null} [value] */ set weird(value) { var ptr0 = isLikeNone(value) ? 0 : passStringToWasm0(value, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -184,7 +184,7 @@ export class Foo { return ret === 0xFFFFFF ? undefined : ret !== 0; } /** - * @param {boolean | undefined} [value] + * @param {boolean | null} [value] */ static set x(value) { wasm.foo_set_x_static(isLikeNone(value) ? 0xFFFFFF : value ? 1 : 0); diff --git a/crates/cli/tests/reference/int128.d.ts b/crates/cli/tests/reference/int128.d.ts index 183cef4758a..ff5bfb22910 100644 --- a/crates/cli/tests/reference/int128.d.ts +++ b/crates/cli/tests/reference/int128.d.ts @@ -2,6 +2,6 @@ /* eslint-disable */ export function echo_i128(a: bigint): bigint; export function echo_u128(a: bigint): bigint; -export function echo_option_i128(a?: bigint): bigint | undefined; -export function echo_option_u128(a?: bigint): bigint | undefined; +export function echo_option_i128(a?: bigint | null): bigint | undefined; +export function echo_option_u128(a?: bigint | null): bigint | undefined; export function throw_i128(): bigint; diff --git a/crates/cli/tests/reference/int128.js b/crates/cli/tests/reference/int128.js index 1df6bb28c26..32de778621c 100644 --- a/crates/cli/tests/reference/int128.js +++ b/crates/cli/tests/reference/int128.js @@ -45,7 +45,7 @@ function isLikeNone(x) { return x === undefined || x === null; } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_i128(a) { @@ -54,7 +54,7 @@ export function echo_option_i128(a) { } /** - * @param {bigint | undefined} [a] + * @param {bigint | null} [a] * @returns {bigint | undefined} */ export function echo_option_u128(a) { diff --git a/crates/cli/tests/reference/optional-args.d.ts b/crates/cli/tests/reference/optional-args.d.ts new file mode 100644 index 00000000000..4f156949356 --- /dev/null +++ b/crates/cli/tests/reference/optional-args.d.ts @@ -0,0 +1,4 @@ +/* tslint:disable */ +/* eslint-disable */ +export function all_optional(a?: number | null, b?: number | null, c?: number | null): void; +export function some_optional(a: number | null | undefined, b: number, c?: number | null): void; diff --git a/crates/cli/tests/reference/optional-args.js b/crates/cli/tests/reference/optional-args.js new file mode 100644 index 00000000000..43ead5d86c1 --- /dev/null +++ b/crates/cli/tests/reference/optional-args.js @@ -0,0 +1,38 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + + +function isLikeNone(x) { + return x === undefined || x === null; +} +/** + * @param {number | null} [a] + * @param {number | null} [b] + * @param {number | null} [c] + */ +export function all_optional(a, b, c) { + wasm.all_optional(isLikeNone(a) ? 0x100000001 : (a) >>> 0, isLikeNone(b) ? 0x100000001 : (b) >>> 0, isLikeNone(c) ? 0x100000001 : (c) >>> 0); +} + +/** + * @param {number | null | undefined} a + * @param {number} b + * @param {number | null} [c] + */ +export function some_optional(a, b, c) { + wasm.some_optional(isLikeNone(a) ? 0x100000001 : (a) >>> 0, b, isLikeNone(c) ? 0x100000001 : (c) >>> 0); +} + +export function __wbindgen_init_externref_table() { + const table = wasm.__wbindgen_export_0; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + ; +}; + diff --git a/crates/cli/tests/reference/optional-args.rs b/crates/cli/tests/reference/optional-args.rs new file mode 100644 index 00000000000..bbc0fa81e39 --- /dev/null +++ b/crates/cli/tests/reference/optional-args.rs @@ -0,0 +1,7 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn all_optional(a: Option, b: Option, c: Option) {} + +#[wasm_bindgen] +pub fn some_optional(a: Option, b: u32, c: Option) {} diff --git a/crates/cli/tests/reference/optional-args.wat b/crates/cli/tests/reference/optional-args.wat new file mode 100644 index 00000000000..a0f6061efbe --- /dev/null +++ b/crates/cli/tests/reference/optional-args.wat @@ -0,0 +1,17 @@ +(module $reference_test.wasm + (type (;0;) (func)) + (type (;1;) (func (param f64 i32 f64))) + (type (;2;) (func (param f64 f64 f64))) + (import "./reference_test_bg.js" "__wbindgen_init_externref_table" (func (;0;) (type 0))) + (func $all_optional (;1;) (type 2) (param f64 f64 f64)) + (func $some_optional (;2;) (type 1) (param f64 i32 f64)) + (table (;0;) 128 externref) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "all_optional" (func $all_optional)) + (export "some_optional" (func $some_optional)) + (export "__wbindgen_export_0" (table 0)) + (export "__wbindgen_start" (func 0)) + (@custom "target_features" (after code) "\04+\0amultivalue+\0fmutable-globals+\0freference-types+\08sign-ext") +) + diff --git a/crates/typescript-tests/src/optional_fields.ts b/crates/typescript-tests/src/optional_fields.ts index 61e1fc05b1b..8d1cf90c64c 100644 --- a/crates/typescript-tests/src/optional_fields.ts +++ b/crates/typescript-tests/src/optional_fields.ts @@ -1,3 +1,10 @@ import * as wbg from '../pkg/typescript_tests'; -const fields: wbg.Fields = { spaceboy: true, free: () => { } }; +const fields: wbg.Fields = null as unknown as wbg.Fields; + +// optional fields read T | undefined +const _hallo: boolean | undefined = fields.hallo; + +// and allow writing T | null | undefined +fields.hallo = undefined; +fields.hallo = null; From c345cc8122ac1c4fffe2c396ffbd2330f797bfdf Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 7 Dec 2024 23:00:21 +0100 Subject: [PATCH 4/7] Added missing type annotation in docs (#4332) --- guide/src/reference/static-js-objects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index 92b699d57c6..c78dc3c261e 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -26,7 +26,7 @@ let COLORS = { #[wasm_bindgen] extern "C" { #[wasm_bindgen(thread_local_v2)] - static COLORS; + static COLORS: JsValue; } fn get_colors() -> JsValue { From 5b38cb1b714450150e5e38991d47bde06a3451e5 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Sat, 7 Dec 2024 23:13:21 +0100 Subject: [PATCH 5/7] Use TypeScript to verify CLI reference output (#4311) --- .github/workflows/main.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5263ee206cb..6909d10dd08 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - run: rustup update --no-self-update stable && rustup default stable - run: rustup component add rustfmt - run: cargo fmt --all -- --check - + # Check TOML style by using Taplo. taplo: name: Taplo @@ -240,6 +240,22 @@ jobs: # WBINDGEN_I_PROMISE_JS_SYNTAX_WORKS_IN_NODE: 1 # - run: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document" + # This checks that the output of the CLI is actually valid JavaScript and TypeScript + test_cli_reference_typescript: + name: Run CLI reference TypeScript check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + - run: npm i -g typescript + - run: npm i --save @types/node @types/deno + - name: Check TypeScript output + run: tsc --noEmit --skipLibCheck --lib esnext,dom $(echo crates/cli/tests/reference/*.d.ts) + - name: Check JavaScript output + run: tsc --noEmit --skipLibCheck --lib esnext,dom --module esnext --allowJs $(echo crates/cli/tests/reference/*.js) + test_native: name: Run native tests runs-on: ubuntu-latest From afea96e83935eb44fea5b48b7b7db3f1bdb472af Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 8 Dec 2024 09:22:15 +0100 Subject: [PATCH 6/7] Fix enabling the same unstable feature twice (#4338) --- src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 73a6147773f..b4e5a6a9b1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,14 +6,17 @@ //! interface. #![no_std] +#![cfg_attr(wasm_bindgen_unstable_test_coverage, feature(coverage_attribute))] #![cfg_attr( - wasm_bindgen_unstable_test_coverage, - feature(coverage_attribute, allow_internal_unstable), - allow(internal_features) + all(not(feature = "std"), target_feature = "atomics"), + feature(thread_local) )] #![cfg_attr( - all(not(feature = "std"), target_feature = "atomics"), - feature(thread_local, allow_internal_unstable), + any( + all(not(feature = "std"), target_feature = "atomics"), + wasm_bindgen_unstable_test_coverage + ), + feature(allow_internal_unstable), allow(internal_features) )] #![allow(coherence_leak_check)] From 54f97c967b194446438a4ea1e899dfd9d2e72cf9 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 8 Dec 2024 09:35:37 +0100 Subject: [PATCH 7/7] Limit scope of `allow(coherence_leak_check)` (#4339) --- src/closure.rs | 2 ++ src/convert/closures.rs | 12 ++++++++---- src/lib.rs | 1 - 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/closure.rs b/src/closure.rs index 95cf2250a89..d5107c34ec9 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -561,6 +561,7 @@ macro_rules! doit { ($( ($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($( + #[allow(coherence_leak_check)] unsafe impl<$($var,)* R> WasmClosure for dyn Fn($($var),*) -> R + 'static where $($var: FromWasmAbi + 'static,)* R: ReturnWasmAbi + 'static, @@ -620,6 +621,7 @@ macro_rules! doit { } } + #[allow(coherence_leak_check)] unsafe impl<$($var,)* R> WasmClosure for dyn FnMut($($var),*) -> R + 'static where $($var: FromWasmAbi + 'static,)* R: ReturnWasmAbi + 'static, diff --git a/src/convert/closures.rs b/src/convert/closures.rs index 1f45f90f58b..536ca686d0a 100644 --- a/src/convert/closures.rs +++ b/src/convert/closures.rs @@ -10,7 +10,8 @@ use crate::throw_str; macro_rules! stack_closures { ($( ($cnt:tt $invoke:ident $invoke_mut:ident $($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*) )*) => ($( - impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (dyn Fn($($var),*) -> R + 'b) + #[allow(coherence_leak_check)] + impl<$($var,)* R> IntoWasmAbi for &'_ (dyn Fn($($var),*) -> R + '_) where $($var: FromWasmAbi,)* R: ReturnWasmAbi { @@ -50,7 +51,8 @@ macro_rules! stack_closures { ret.return_abi().into() } - impl<'a, $($var,)* R> WasmDescribe for dyn Fn($($var),*) -> R + 'a + #[allow(coherence_leak_check)] + impl<$($var,)* R> WasmDescribe for dyn Fn($($var),*) -> R + '_ where $($var: FromWasmAbi,)* R: ReturnWasmAbi { @@ -65,7 +67,8 @@ macro_rules! stack_closures { } } - impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (dyn FnMut($($var),*) -> R + 'b) + #[allow(coherence_leak_check)] + impl<$($var,)* R> IntoWasmAbi for &'_ mut (dyn FnMut($($var),*) -> R + '_) where $($var: FromWasmAbi,)* R: ReturnWasmAbi { @@ -105,7 +108,8 @@ macro_rules! stack_closures { ret.return_abi().into() } - impl<'a, $($var,)* R> WasmDescribe for dyn FnMut($($var),*) -> R + 'a + #[allow(coherence_leak_check)] + impl<$($var,)* R> WasmDescribe for dyn FnMut($($var),*) -> R + '_ where $($var: FromWasmAbi,)* R: ReturnWasmAbi { diff --git a/src/lib.rs b/src/lib.rs index b4e5a6a9b1f..3aac1273ae7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,6 @@ feature(allow_internal_unstable), allow(internal_features) )] -#![allow(coherence_leak_check)] #![doc(html_root_url = "https://docs.rs/wasm-bindgen/0.2")] extern crate alloc;