Skip to content

Commit 9b81f4b

Browse files
authored
Add experimental support for Symbol.dispose (#4118)
Via `WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE`.
1 parent ffdf0de commit 9b81f4b

File tree

10 files changed

+145
-0
lines changed

10 files changed

+145
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* Add bindings to `RTCRtpTransceiverDirection.stopped`.
1515
[#4102](https://github.com/rustwasm/wasm-bindgen/pull/4102)
1616

17+
* Added experimental support for `Symbol.dispose` via `WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE`.
18+
[#4118](https://github.com/rustwasm/wasm-bindgen/pull/4118)
19+
1720
### Fixed
1821

1922
* Fixed linked modules emitting snippet files when not using `--split-linked-modules`.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ members = [
7979
"examples/deno",
8080
"examples/dom",
8181
"examples/duck-typed-interfaces",
82+
"examples/explicit-resource-management",
8283
"examples/fetch",
8384
"examples/guide-supported-types-examples",
8485
"examples/hello_world",

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,16 @@ impl<'a> Context<'a> {
10841084
wasm_bindgen_shared::free_function(name),
10851085
));
10861086
ts_dst.push_str(" free(): void;\n");
1087+
if self.config.symbol_dispose {
1088+
dst.push_str(
1089+
"
1090+
[Symbol.dispose]() {{
1091+
this.free();
1092+
}}
1093+
",
1094+
);
1095+
ts_dst.push_str(" [Symbol.dispose](): void;\n");
1096+
}
10871097
dst.push_str(&class.contents);
10881098
ts_dst.push_str(&class.typescript);
10891099

@@ -1486,6 +1496,14 @@ impl<'a> Context<'a> {
14861496
Ok(ret)
14871497
}
14881498

1499+
fn expose_symbol_dispose(&mut self) -> Result<(), Error> {
1500+
if !self.should_write_global("symbol_dispose") {
1501+
return Ok(());
1502+
}
1503+
self.global("if(!Symbol.dispose) { Symbol.dispose = Symbol('Symbol.dispose'); }");
1504+
Ok(())
1505+
}
1506+
14891507
fn expose_text_encoder(&mut self) -> Result<(), Error> {
14901508
if !self.should_write_global("text_encoder") {
14911509
return Ok(());
@@ -2476,6 +2494,10 @@ impl<'a> Context<'a> {
24762494

24772495
pub fn generate(&mut self) -> Result<(), Error> {
24782496
self.prestore_global_import_identifiers()?;
2497+
// conditionally override Symbol.dispose
2498+
if self.config.symbol_dispose && !self.aux.structs.is_empty() {
2499+
self.expose_symbol_dispose()?;
2500+
}
24792501
for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
24802502
let instrs = match &adapter.kind {
24812503
AdapterKind::Import { .. } => continue,

crates/cli-support/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub struct Bindgen {
4242
multi_value: bool,
4343
encode_into: EncodeInto,
4444
split_linked_modules: bool,
45+
symbol_dispose: bool,
4546
}
4647

4748
pub struct Output {
@@ -88,6 +89,7 @@ impl Bindgen {
8889
let externref =
8990
env::var("WASM_BINDGEN_ANYREF").is_ok() || env::var("WASM_BINDGEN_EXTERNREF").is_ok();
9091
let multi_value = env::var("WASM_BINDGEN_MULTI_VALUE").is_ok();
92+
let symbol_dispose = env::var("WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE").is_ok();
9193
Bindgen {
9294
input: Input::None,
9395
out_name: None,
@@ -109,6 +111,7 @@ impl Bindgen {
109111
encode_into: EncodeInto::Test,
110112
omit_default_module_path: true,
111113
split_linked_modules: false,
114+
symbol_dispose,
112115
}
113116
}
114117

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
authors = ["The wasm-bindgen Developers"]
3+
edition = "2021"
4+
name = "explicit-resource-management"
5+
publish = false
6+
version = "0.0.0"
7+
8+
[lib]
9+
crate-type = ["cdylib"]
10+
11+
[dependencies]
12+
wasm-bindgen = { path = "../../" }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Using Explicit Resource Management (via Deno)
2+
3+
You can build the example with
4+
5+
```sh
6+
$ ./build.sh
7+
```
8+
9+
and test it with
10+
11+
```sh
12+
$ deno run --allow-read test.ts
13+
```
14+
15+
The `--allow-read` flag is needed because the Wasm file is read during runtime.
16+
This will be fixed when https://github.com/denoland/deno/issues/2552 is resolved.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/sh
2+
3+
set -eux
4+
5+
cargo build --target wasm32-unknown-unknown --release
6+
WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE=1 cargo run --package wasm-bindgen-cli --bin wasm-bindgen -- \
7+
--out-dir pkg --target deno ${CARGO_TARGET_DIR:-../../target}/wasm32-unknown-unknown/release/explicit_resource_management.wasm
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/explicit-resource-management.wasm
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use std::alloc::{GlobalAlloc, Layout, System};
2+
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
3+
use wasm_bindgen::prelude::*;
4+
5+
// simple counting allocator tracking
6+
struct Counter;
7+
8+
static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
9+
10+
unsafe impl GlobalAlloc for Counter {
11+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
12+
let ret = System.alloc(layout);
13+
if !ret.is_null() {
14+
ALLOCATED.fetch_add(layout.size(), Relaxed);
15+
}
16+
ret
17+
}
18+
19+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
20+
System.dealloc(ptr, layout);
21+
ALLOCATED.fetch_sub(layout.size(), Relaxed);
22+
}
23+
}
24+
25+
#[global_allocator]
26+
static A: Counter = Counter;
27+
28+
#[wasm_bindgen]
29+
pub fn current_allocation() -> usize {
30+
ALLOCATED.load(Relaxed)
31+
}
32+
33+
// lifted from the `console_log` example
34+
#[wasm_bindgen]
35+
extern "C" {
36+
#[wasm_bindgen(js_namespace = console)]
37+
fn log(s: &str);
38+
}
39+
40+
#[wasm_bindgen]
41+
pub struct MyStruct {
42+
x: Vec<i64>,
43+
y: Vec<i64>,
44+
name: String,
45+
}
46+
47+
#[wasm_bindgen]
48+
impl MyStruct {
49+
#[wasm_bindgen(constructor)]
50+
pub fn new(name: String) -> MyStruct {
51+
Self {
52+
name,
53+
x: (0..50).collect(),
54+
y: (0..50).collect(),
55+
}
56+
}
57+
}
58+
59+
impl Drop for MyStruct {
60+
fn drop(&mut self) {
61+
log(&format!("Goodbye from {}!", self.name)); // should output "Goodbye from Rust!"
62+
}
63+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { current_allocation, MyStruct } from "./pkg/explicit_resource_management.js";
2+
3+
const initialAllocation = current_allocation();
4+
let referrent = {};
5+
console.log('Before scope: ', initialAllocation);
6+
7+
{
8+
using foo = new MyStruct("Rust");
9+
// force foo to be treated as live by implicit memory management (FinalizationRegistry/GC)
10+
// by retaining a reference that outlives the scope block (for the purposes of proving
11+
// Symbol.dispose is called on scope exit).
12+
referrent['foo'] = foo;
13+
console.log('After construction, but before scope exit: ', current_allocation());
14+
}
15+
const afterDisposeAllocation = current_allocation();
16+
console.log('After scope exit: ', afterDisposeAllocation);
17+
console.log(referrent);

0 commit comments

Comments
 (0)