Skip to content

Commit 3e469b2

Browse files
authored
Deprecate --weak-refs in favor of run-time detection (#3822)
* Deprecate `--weak-refs` in favor of run-time detection * Generate empty functions instead * Fix integration tests * Improve `Closure::into_js_value()` docs
1 parent 1f7942d commit 3e469b2

File tree

10 files changed

+50
-106
lines changed

10 files changed

+50
-106
lines changed

.github/workflows/main.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,7 @@ jobs:
9090
- run: cargo test --target wasm32-unknown-unknown -p wasm-bindgen-futures
9191
- run: cargo test --target wasm32-unknown-unknown --test wasm
9292
env:
93-
WASM_BINDGEN_WEAKREF: 1
94-
- run: cargo test --target wasm32-unknown-unknown --test wasm
95-
env:
96-
WASM_BINDGEN_WEAKREF: 1
9793
WASM_BINDGEN_NO_DEBUG: 1
98-
- run: cargo test --target wasm32-unknown-unknown --test wasm --features serde-serialize
99-
env:
100-
WASM_BINDGEN_WEAKREF: 1
10194
- run: cargo test --target wasm32-unknown-unknown
10295
env:
10396
WASM_BINDGEN_EXTERNREF: 1

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
* Updated the WebGPU WebIDL to the current draft as of 2024-01-30. Note that this retains the previous update's workaround for `GPUPipelineError`, and holds back an update to the `buffer` argument of the `GPUQueue.{writeBuffer,writeTexture}` methods.
3535
[#3816](https://github.com/rustwasm/wasm-bindgen/pull/3816)
3636

37+
* Depreate `--weak-refs` and `WASM_BINDGEN_WEAKREF` in favor of automatic run-time detection.
38+
[#3822](https://github.com/rustwasm/wasm-bindgen/pull/3822)
39+
3740
### Fixed
3841

3942
* Fixed UB when freeing strings received from JS if not using the default allocator.

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

Lines changed: 23 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -921,18 +921,12 @@ impl<'a> Context<'a> {
921921
"
922922
static __wrap(ptr) {{
923923
ptr = ptr >>> 0;
924-
const obj = Object.create({}.prototype);
924+
const obj = Object.create({name}.prototype);
925925
obj.__wbg_ptr = ptr;
926-
{}
926+
{name}Finalization.register(obj, obj.__wbg_ptr, obj);
927927
return obj;
928928
}}
929-
",
930-
name,
931-
if self.config.weak_refs {
932-
format!("{}Finalization.register(obj, obj.__wbg_ptr, obj);", name)
933-
} else {
934-
String::new()
935-
},
929+
"
936930
));
937931
}
938932

@@ -950,13 +944,13 @@ impl<'a> Context<'a> {
950944
));
951945
}
952946

953-
if self.config.weak_refs {
954-
self.global(&format!(
955-
"const {}Finalization = new FinalizationRegistry(ptr => wasm.{}(ptr >>> 0));",
956-
name,
957-
wasm_bindgen_shared::free_function(name),
958-
));
959-
}
947+
self.global(&format!(
948+
"
949+
const {name}Finalization = (typeof FinalizationRegistry === 'undefined')
950+
? {{ register: () => {{}}, unregister: () => {{}} }}
951+
: new FinalizationRegistry(ptr => wasm.{}(ptr >>> 0));",
952+
wasm_bindgen_shared::free_function(name),
953+
));
960954

961955
// If the class is inspectable, generate `toJSON` and `toString`
962956
// to expose all readable properties of the class. Otherwise,
@@ -1021,7 +1015,7 @@ impl<'a> Context<'a> {
10211015
__destroy_into_raw() {{
10221016
const ptr = this.__wbg_ptr;
10231017
this.__wbg_ptr = 0;
1024-
{}
1018+
{name}Finalization.unregister(this);
10251019
return ptr;
10261020
}}
10271021
@@ -1030,11 +1024,6 @@ impl<'a> Context<'a> {
10301024
wasm.{}(ptr);
10311025
}}
10321026
",
1033-
if self.config.weak_refs {
1034-
format!("{}Finalization.unregister(this);", name)
1035-
} else {
1036-
String::new()
1037-
},
10381027
wasm_bindgen_shared::free_function(name),
10391028
));
10401029
ts_dst.push_str(" free(): void;\n");
@@ -2121,15 +2110,7 @@ impl<'a> Context<'a> {
21212110

21222111
let table = self.export_function_table()?;
21232112

2124-
let (register, unregister) = if self.config.weak_refs {
2125-
self.expose_closure_finalization()?;
2126-
(
2127-
"CLOSURE_DTORS.register(real, state, state);",
2128-
"CLOSURE_DTORS.unregister(state)",
2129-
)
2130-
} else {
2131-
("", "")
2132-
};
2113+
self.expose_closure_finalization()?;
21332114

21342115
// For mutable closures they can't be invoked recursively.
21352116
// To handle that we swap out the `this.a` pointer with zero
@@ -2152,20 +2133,17 @@ impl<'a> Context<'a> {
21522133
}} finally {{
21532134
if (--state.cnt === 0) {{
21542135
wasm.{table}.get(state.dtor)(a, state.b);
2155-
{unregister}
2136+
CLOSURE_DTORS.unregister(state);
21562137
}} else {{
21572138
state.a = a;
21582139
}}
21592140
}}
21602141
}};
21612142
real.original = state;
2162-
{register}
2143+
CLOSURE_DTORS.register(real, state, state);
21632144
return real;
21642145
}}
21652146
",
2166-
table = table,
2167-
register = register,
2168-
unregister = unregister,
21692147
));
21702148

21712149
Ok(())
@@ -2178,15 +2156,7 @@ impl<'a> Context<'a> {
21782156

21792157
let table = self.export_function_table()?;
21802158

2181-
let (register, unregister) = if self.config.weak_refs {
2182-
self.expose_closure_finalization()?;
2183-
(
2184-
"CLOSURE_DTORS.register(real, state, state);",
2185-
"CLOSURE_DTORS.unregister(state)",
2186-
)
2187-
} else {
2188-
("", "")
2189-
};
2159+
self.expose_closure_finalization()?;
21902160

21912161
// For shared closures they can be invoked recursively so we
21922162
// just immediately pass through `this.a`. If we end up
@@ -2208,18 +2178,15 @@ impl<'a> Context<'a> {
22082178
if (--state.cnt === 0) {{
22092179
wasm.{table}.get(state.dtor)(state.a, state.b);
22102180
state.a = 0;
2211-
{unregister}
2181+
CLOSURE_DTORS.unregister(state);
22122182
}}
22132183
}}
22142184
}};
22152185
real.original = state;
2216-
{register}
2186+
CLOSURE_DTORS.register(real, state, state);
22172187
return real;
22182188
}}
22192189
",
2220-
table = table,
2221-
register = register,
2222-
unregister = unregister,
22232190
));
22242191

22252192
Ok(())
@@ -2229,15 +2196,15 @@ impl<'a> Context<'a> {
22292196
if !self.should_write_global("closure_finalization") {
22302197
return Ok(());
22312198
}
2232-
assert!(self.config.weak_refs);
22332199
let table = self.export_function_table()?;
22342200
self.global(&format!(
22352201
"
2236-
const CLOSURE_DTORS = new FinalizationRegistry(state => {{
2237-
wasm.{}.get(state.dtor)(state.a, state.b)
2238-
}});
2239-
",
2240-
table
2202+
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
2203+
? {{ register: () => {{}}, unregister: () => {{}} }}
2204+
: new FinalizationRegistry(state => {{
2205+
wasm.{table}.get(state.dtor)(state.a, state.b)
2206+
}});
2207+
"
22412208
));
22422209

22432210
Ok(())

crates/cli-support/src/lib.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ pub struct Bindgen {
3535
remove_producers_section: bool,
3636
omit_default_module_path: bool,
3737
emit_start: bool,
38-
// Experimental support for weakrefs, an upcoming ECMAScript feature.
39-
// Currently only enable-able through an env var.
40-
weak_refs: bool,
4138
// Support for the wasm threads proposal, transforms the wasm module to be
4239
// "ready to be instantiated on any thread"
4340
threads: wasm_bindgen_threads_xform::Config,
@@ -106,7 +103,6 @@ impl Bindgen {
106103
remove_name_section: false,
107104
remove_producers_section: false,
108105
emit_start: true,
109-
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
110106
threads: threads_config(),
111107
externref,
112108
multi_value,
@@ -126,11 +122,6 @@ impl Bindgen {
126122
self
127123
}
128124

129-
pub fn weak_refs(&mut self, enable: bool) -> &mut Bindgen {
130-
self.weak_refs = enable;
131-
self
132-
}
133-
134125
pub fn reference_types(&mut self, enable: bool) -> &mut Bindgen {
135126
self.externref = enable;
136127
self

crates/cli/src/bin/wasm-bindgen.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Options:
3939
--nodejs Deprecated, use `--target nodejs`
4040
--web Deprecated, use `--target web`
4141
--no-modules Deprecated, use `--target no-modules`
42-
--weak-refs Enable usage of the JS weak references proposal
42+
--weak-refs Deprecated, is runtime-detected
4343
--reference-types Enable usage of WebAssembly reference types
4444
-V --version Print the version number of wasm-bindgen
4545
@@ -126,9 +126,6 @@ fn rmain(args: &Args) -> Result<(), Error> {
126126
.omit_imports(args.flag_omit_imports)
127127
.omit_default_module_path(args.flag_omit_default_module_path)
128128
.split_linked_modules(args.flag_split_linked_modules);
129-
if let Some(true) = args.flag_weak_refs {
130-
b.weak_refs(true);
131-
}
132129
if let Some(true) = args.flag_reference_types {
133130
b.reference_types(true);
134131
}

crates/cli/tests/reference/builder.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ function getStringFromWasm0(ptr, len) {
2323
ptr = ptr >>> 0;
2424
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
2525
}
26+
27+
const ClassBuilderFinalization = (typeof FinalizationRegistry === 'undefined')
28+
? { register: () => {}, unregister: () => {} }
29+
: new FinalizationRegistry(ptr => wasm.__wbg_classbuilder_free(ptr >>> 0));
2630
/**
2731
*/
2832
export class ClassBuilder {
@@ -31,14 +35,14 @@ export class ClassBuilder {
3135
ptr = ptr >>> 0;
3236
const obj = Object.create(ClassBuilder.prototype);
3337
obj.__wbg_ptr = ptr;
34-
38+
ClassBuilderFinalization.register(obj, obj.__wbg_ptr, obj);
3539
return obj;
3640
}
3741

3842
__destroy_into_raw() {
3943
const ptr = this.__wbg_ptr;
4044
this.__wbg_ptr = 0;
41-
45+
ClassBuilderFinalization.unregister(this);
4246
return ptr;
4347
}
4448

crates/cli/tests/reference/constructor.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ function getStringFromWasm0(ptr, len) {
2323
ptr = ptr >>> 0;
2424
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
2525
}
26+
27+
const ClassConstructorFinalization = (typeof FinalizationRegistry === 'undefined')
28+
? { register: () => {}, unregister: () => {} }
29+
: new FinalizationRegistry(ptr => wasm.__wbg_classconstructor_free(ptr >>> 0));
2630
/**
2731
*/
2832
export class ClassConstructor {
2933

3034
__destroy_into_raw() {
3135
const ptr = this.__wbg_ptr;
3236
this.__wbg_ptr = 0;
33-
37+
ClassConstructorFinalization.unregister(this);
3438
return ptr;
3539
}
3640

guide/src/reference/cli.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,6 @@ When generating bundler-compatible code (see the section on [deployment]) this
8484
indicates that the bundled code is always intended to go into a browser so a few
8585
checks for Node.js can be elided.
8686

87-
### `--weak-refs`
88-
89-
Enables usage of the [TC39 Weak References
90-
proposal](https://github.com/tc39/proposal-weakrefs), ensuring that all Rust
91-
memory is eventually deallocated regardless of whether you're calling `free` or
92-
not. This is off-by-default while we're waiting for support to percolate into
93-
all major browsers. For more information see the [documentation about weak
94-
references](./weak-references.md).
95-
9687
### `--reference-types`
9788

9889
Enables usage of the [WebAssembly References Types
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# Support for Weak References
22

3-
By default wasm-bindgen does not use the [TC39 weak references
4-
proposal](https://github.com/tc39/proposal-weakrefs). This proposal just
5-
advanced to stage 4 at the time of this writing, but it will still stake some
6-
time for support to percolate into all the major browsers.
3+
By default wasm-bindgen does use the [TC39 weak references
4+
proposal](https://github.com/tc39/proposal-weakrefs) if support is detected.
5+
At the time of this writing all major browsers do support it.
76

87
Without weak references your JS integration may be susceptible to memory leaks
98
in Rust, for example:
@@ -15,9 +14,8 @@ in Rust, for example:
1514
* Rust closures have `Closure::{into_js_value,forget}` methods which explicitly
1615
do not free the underlying memory.
1716

18-
These issues are all solved with the weak references proposal in JS. The
19-
`--weak-refs` flag to the `wasm-bindgen` CLI will enable usage of
20-
`FinalizationRegistry` to ensure that all memory is cleaned up, regardless of
17+
These issues are all solved with the weak references proposal in JS.
18+
`FinalizationRegistry` will ensure that all memory is cleaned up, regardless of
2119
whether it's explicitly deallocated or not. Note that explicit deallocation
2220
is always a possibility and supported, but if it's not called then memory will
23-
still be automatically deallocated with the `--weak-refs` flag.
21+
still be automatically deallocated if `FinalizationRegistry` support is detected.

src/closure.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,16 +356,12 @@ where
356356
/// lifetime dynamically managed by the JS GC. This function can be used
357357
/// to drop this `Closure` while keeping the associated JS function still
358358
/// valid.
359-
///
360-
/// By default this function will leak memory. This can be dangerous if this
361-
/// function is called many times in an application because the memory leak
362-
/// will overwhelm the page quickly and crash the wasm.
363-
///
364-
/// If the browser, however, supports weak references, then this function
365-
/// will not leak memory. Instead the Rust memory will be reclaimed when the
366-
/// JS closure is GC'd. Weak references are not enabled by default since
367-
/// they're still a proposal for the JS standard. They can be enabled with
368-
/// `WASM_BINDGEN_WEAKREF=1` when running `wasm-bindgen`, however.
359+
///
360+
/// If the platform supports weak references, the Rust memory will be
361+
/// reclaimed when the JS closure is GC'd. If weak references is not
362+
/// supported, this can be dangerous if this function is called many times
363+
/// in an application because the memory leak will overwhelm the page
364+
/// quickly and crash the wasm.
369365
pub fn into_js_value(self) -> JsValue {
370366
let idx = self.js.idx;
371367
mem::forget(self);

0 commit comments

Comments
 (0)