Skip to content

Commit 5572877

Browse files
Merge branch 'main' into var-name-binding
2 parents 6e5f2e9 + af7c106 commit 5572877

File tree

62 files changed

+1708
-1003
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1708
-1003
lines changed

.github/workflows/main.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- run: rustup update --no-self-update stable && rustup default stable
3030
- run: rustup component add rustfmt
3131
- run: cargo fmt --all -- --check
32-
32+
3333
# Check TOML style by using Taplo.
3434
taplo:
3535
name: Taplo
@@ -240,6 +240,22 @@ jobs:
240240
# WBINDGEN_I_PROMISE_JS_SYNTAX_WORKS_IN_NODE: 1
241241
# - run: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document"
242242

243+
# This checks that the output of the CLI is actually valid JavaScript and TypeScript
244+
test_cli_reference_typescript:
245+
name: Run CLI reference TypeScript check
246+
runs-on: ubuntu-latest
247+
steps:
248+
- uses: actions/checkout@v4
249+
- uses: actions/setup-node@v4
250+
with:
251+
node-version: 'lts/*'
252+
- run: npm i -g typescript
253+
- run: npm i --save @types/node @types/deno
254+
- name: Check TypeScript output
255+
run: tsc --noEmit --skipLibCheck --lib esnext,dom $(echo crates/cli/tests/reference/*.d.ts)
256+
- name: Check JavaScript output
257+
run: tsc --noEmit --skipLibCheck --lib esnext,dom --module esnext --allowJs $(echo crates/cli/tests/reference/*.js)
258+
243259
test_native:
244260
name: Run native tests
245261
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
# `wasm-bindgen` Change Log
22
--------------------------------------------------------------------------------
33

4+
## Unreleased
5+
6+
### Added
7+
8+
* Add a `copy_to_uninit()` method to all `TypedArray`s. It takes `&mut [MaybeUninit<T>]` and returns `&mut [T]`.
9+
[#4340](https://github.com/rustwasm/wasm-bindgen/pull/4340)
10+
11+
### Changed
12+
13+
* Optional parameters are now typed as `T | undefined | null` to reflect the actual JS behavior.
14+
[#4188](https://github.com/rustwasm/wasm-bindgen/pull/4188)
15+
16+
### Fixed
17+
18+
- Fixed using [JavaScript keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords) as identifiers not being handled correctly.
19+
[#4329](https://github.com/rustwasm/wasm-bindgen/pull/4329)
20+
21+
- Using JS keywords as `struct` and `enum` names will now error at compile time, instead of causing invalid JS code gen.
22+
- Using JS keywords that are not valid to call or access properties on will now error at compile time, instead of causing invalid JS code gen if used as:
23+
1. The first part of a `js_namespace` on imports.
24+
2. The name of an imported type or constant if the type or constant does not have a `js_namespace` or `module` attribute.
25+
3. The name of an imported function if the function is not a method and does not have a `js_namespace` or `module` attribute.
26+
- Using JS keywords on imports in places other than the above will no longer cause the keywords to be escaped as `_{keyword}`.
27+
28+
--------------------------------------------------------------------------------
29+
430
## [0.2.99](https://github.com/rustwasm/wasm-bindgen/compare/0.2.98...0.2.99)
531

632
Released 2024-12-07

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@ features = ["serde-serialize"]
2222
test = false
2323

2424
[features]
25-
default = ["spans", "std"]
25+
default = ["std"]
2626
enable-interning = ["std"]
2727
serde-serialize = ["serde", "serde_json", "std"]
28-
spans = ["wasm-bindgen-macro/spans"]
28+
spans = []
2929
std = ["wasm-bindgen-macro/std", "once_cell/std"]
3030

3131
# Whether or not the `#[wasm_bindgen]` macro is strict and generates an error on
3232
# all unused attributes
3333
strict-macro = ["wasm-bindgen-macro/strict-macro"]
3434

35-
# Enables gg-alloc as system allocator when using wasm-bindgen-test to check that large pointers
35+
# INTERNAL ONLY: Enables gg-alloc as system allocator when using wasm-bindgen-test to check that large pointers
3636
# are handled correctly
3737
gg-alloc = ["wasm-bindgen-test/gg-alloc"]
3838

39-
# This is only for debugging wasm-bindgen! No stability guarantees, so enable
39+
# INTERNAL ONLY: This is only for debugging wasm-bindgen! No stability guarantees, so enable
4040
# this at your own peril!
4141
xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_generated_code"]
4242

crates/backend/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ version = "0.2.99"
1616
[features]
1717
default = ["std"]
1818
extra-traits = ["syn/extra-traits"]
19-
spans = []
2019
std = []
2120

2221
[dependencies]

crates/cli-support/src/js/binding.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,15 @@ impl<'a, 'b> Builder<'a, 'b> {
324324
let mut ts = String::new();
325325
match ty {
326326
AdapterType::Option(ty) if omittable => {
327+
// e.g. `foo?: string | null`
327328
arg.push_str("?: ");
328-
adapter2ts(ty, &mut ts, Some(&mut ts_refs));
329+
adapter2ts(ty, TypePosition::Argument, &mut ts, Some(&mut ts_refs));
330+
ts.push_str(" | null");
329331
}
330332
ty => {
331333
omittable = false;
332334
arg.push_str(": ");
333-
adapter2ts(ty, &mut ts, Some(&mut ts_refs));
335+
adapter2ts(ty, TypePosition::Argument, &mut ts, Some(&mut ts_refs));
334336
}
335337
}
336338
arg.push_str(&ts);
@@ -366,7 +368,12 @@ impl<'a, 'b> Builder<'a, 'b> {
366368
let mut ret = String::new();
367369
match result_tys.len() {
368370
0 => ret.push_str("void"),
369-
1 => adapter2ts(&result_tys[0], &mut ret, Some(&mut ts_refs)),
371+
1 => adapter2ts(
372+
&result_tys[0],
373+
TypePosition::Return,
374+
&mut ret,
375+
Some(&mut ts_refs),
376+
),
370377
_ => ret.push_str("[any]"),
371378
}
372379
if asyncness {
@@ -398,16 +405,18 @@ impl<'a, 'b> Builder<'a, 'b> {
398405
for (name, ty) in fn_arg_names.iter().zip(arg_tys).rev() {
399406
let mut arg = "@param {".to_string();
400407

401-
adapter2ts(ty, &mut arg, None);
402-
arg.push_str("} ");
403408
match ty {
404-
AdapterType::Option(..) if omittable => {
409+
AdapterType::Option(ty) if omittable => {
410+
adapter2ts(ty, TypePosition::Argument, &mut arg, None);
411+
arg.push_str(" | null} ");
405412
arg.push('[');
406413
arg.push_str(name);
407414
arg.push(']');
408415
}
409416
_ => {
410417
omittable = false;
418+
adapter2ts(ty, TypePosition::Argument, &mut arg, None);
419+
arg.push_str("} ");
411420
arg.push_str(name);
412421
}
413422
}
@@ -419,7 +428,7 @@ impl<'a, 'b> Builder<'a, 'b> {
419428

420429
if let (Some(name), Some(ty)) = (variadic_arg, arg_tys.last()) {
421430
ret.push_str("@param {...");
422-
adapter2ts(ty, &mut ret, None);
431+
adapter2ts(ty, TypePosition::Argument, &mut ret, None);
423432
ret.push_str("} ");
424433
ret.push_str(name);
425434
ret.push('\n');
@@ -1537,7 +1546,18 @@ impl Invocation {
15371546
}
15381547
}
15391548

1540-
fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet<TsReference>>) {
1549+
#[derive(Debug, Clone, Copy)]
1550+
enum TypePosition {
1551+
Argument,
1552+
Return,
1553+
}
1554+
1555+
fn adapter2ts(
1556+
ty: &AdapterType,
1557+
position: TypePosition,
1558+
dst: &mut String,
1559+
refs: Option<&mut HashSet<TsReference>>,
1560+
) {
15411561
match ty {
15421562
AdapterType::I32
15431563
| AdapterType::S8
@@ -1559,8 +1579,11 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet<TsRe
15591579
AdapterType::Bool => dst.push_str("boolean"),
15601580
AdapterType::Vector(kind) => dst.push_str(&kind.js_ty()),
15611581
AdapterType::Option(ty) => {
1562-
adapter2ts(ty, dst, refs);
1563-
dst.push_str(" | undefined");
1582+
adapter2ts(ty, position, dst, refs);
1583+
dst.push_str(match position {
1584+
TypePosition::Argument => " | null | undefined",
1585+
TypePosition::Return => " | undefined",
1586+
});
15641587
}
15651588
AdapterType::NamedExternref(name) => dst.push_str(name),
15661589
AdapterType::Struct(name) => dst.push_str(name),

crates/cli-support/src/js/mod.rs

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ struct FieldAccessor {
120120
is_optional: bool,
121121
}
122122

123+
/// Different JS constructs that can be exported.
124+
enum ExportJs<'a> {
125+
/// A class of the form `class Name {...}`.
126+
Class(&'a str),
127+
/// An anonymous function expression of the form `function(...) {...}`.
128+
///
129+
/// Note that the function name is not included in the string.
130+
Function(&'a str),
131+
/// An arbitrary JS expression.
132+
Expression(&'a str),
133+
}
134+
123135
const INITIAL_HEAP_VALUES: &[&str] = &["undefined", "null", "true", "false"];
124136
// Must be kept in sync with `src/lib.rs` of the `wasm-bindgen` crate
125137
const INITIAL_HEAP_OFFSET: usize = 128;
@@ -163,38 +175,46 @@ impl<'a> Context<'a> {
163175
fn export(
164176
&mut self,
165177
export_name: &str,
166-
contents: &str,
178+
export: ExportJs,
167179
comments: Option<&str>,
168180
) -> Result<(), Error> {
169181
let definition_name = self.generate_identifier(export_name);
170-
if contents.starts_with("class") && definition_name != export_name {
182+
if matches!(export, ExportJs::Class(_)) && definition_name != export_name {
171183
bail!("cannot shadow already defined class `{}`", export_name);
172184
}
173185

174-
let contents = contents.trim();
186+
// write out comments
175187
if let Some(c) = comments {
176188
self.globals.push_str(c);
177189
}
190+
178191
let global = match self.config.mode {
179-
OutputMode::Node { module: false } => {
180-
if contents.starts_with("class") {
181-
format!("{}\nmodule.exports.{1} = {1};\n", contents, export_name)
182-
} else {
183-
format!("module.exports.{} = {};\n", export_name, contents)
192+
OutputMode::Node { module: false } => match export {
193+
ExportJs::Class(class) => {
194+
format!("{}\nmodule.exports.{1} = {1};\n", class, export_name)
184195
}
185-
}
186-
OutputMode::NoModules { .. } => {
187-
if contents.starts_with("class") {
188-
format!("{}\n__exports.{1} = {1};\n", contents, export_name)
189-
} else {
190-
format!("__exports.{} = {};\n", export_name, contents)
196+
ExportJs::Function(expr) | ExportJs::Expression(expr) => {
197+
format!("module.exports.{} = {};\n", export_name, expr)
191198
}
192-
}
199+
},
200+
OutputMode::NoModules { .. } => match export {
201+
ExportJs::Class(class) => {
202+
format!("{}\n__exports.{1} = {1};\n", class, export_name)
203+
}
204+
ExportJs::Function(expr) | ExportJs::Expression(expr) => {
205+
format!("__exports.{} = {};\n", export_name, expr)
206+
}
207+
},
193208
OutputMode::Bundler { .. }
194209
| OutputMode::Node { module: true }
195210
| OutputMode::Web
196-
| OutputMode::Deno => {
197-
if let Some(body) = contents.strip_prefix("function") {
211+
| OutputMode::Deno => match export {
212+
ExportJs::Class(class) => {
213+
assert_eq!(export_name, definition_name);
214+
format!("export {}\n", class)
215+
}
216+
ExportJs::Function(function) => {
217+
let body = function.strip_prefix("function").unwrap();
198218
if export_name == definition_name {
199219
format!("export function {}{}\n", export_name, body)
200220
} else {
@@ -203,14 +223,12 @@ impl<'a> Context<'a> {
203223
definition_name, body, definition_name, export_name,
204224
)
205225
}
206-
} else if contents.starts_with("class") {
207-
assert_eq!(export_name, definition_name);
208-
format!("export {}\n", contents)
209-
} else {
226+
}
227+
ExportJs::Expression(expr) => {
210228
assert_eq!(export_name, definition_name);
211-
format!("export const {} = {};\n", export_name, contents)
229+
format!("export const {} = {};\n", export_name, expr)
212230
}
213-
}
231+
},
214232
};
215233
self.global(&global);
216234
Ok(())
@@ -1169,10 +1187,10 @@ __wbg_set_wasm(wasm);"
11691187

11701188
self.write_class_field_types(class, &mut ts_dst);
11711189

1172-
dst.push_str("}\n");
1190+
dst.push('}');
11731191
ts_dst.push_str("}\n");
11741192

1175-
self.export(name, &dst, Some(&class.comments))?;
1193+
self.export(name, ExportJs::Class(&dst), Some(&class.comments))?;
11761194

11771195
if class.generate_typescript {
11781196
self.typescript.push_str(&class.comments);
@@ -2872,7 +2890,11 @@ __wbg_set_wasm(wasm);"
28722890
self.typescript.push_str(";\n");
28732891
}
28742892

2875-
self.export(name, &format!("function{}", code), Some(&js_docs))?;
2893+
self.export(
2894+
name,
2895+
ExportJs::Function(&format!("function{}", code)),
2896+
Some(&js_docs),
2897+
)?;
28762898
self.globals.push('\n');
28772899
}
28782900
AuxExportKind::Constructor(class) => {
@@ -3995,7 +4017,7 @@ __wbg_set_wasm(wasm);"
39954017

39964018
self.export(
39974019
&enum_.name,
3998-
&format!("Object.freeze({{\n{}}})", variants),
4020+
ExportJs::Expression(&format!("Object.freeze({{\n{}}})", variants)),
39994021
Some(&docs),
40004022
)?;
40014023

0 commit comments

Comments
 (0)