Skip to content

Commit

Permalink
Add experimental support for Symbol.dispose (#4118)
Browse files Browse the repository at this point in the history
Via `WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE`.
  • Loading branch information
H-Plus-Time authored Sep 28, 2024
1 parent ffdf0de commit 9b81f4b
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
* Add bindings to `RTCRtpTransceiverDirection.stopped`.
[#4102](https://github.com/rustwasm/wasm-bindgen/pull/4102)

* Added experimental support for `Symbol.dispose` via `WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE`.
[#4118](https://github.com/rustwasm/wasm-bindgen/pull/4118)

### Fixed

* Fixed linked modules emitting snippet files when not using `--split-linked-modules`.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ members = [
"examples/deno",
"examples/dom",
"examples/duck-typed-interfaces",
"examples/explicit-resource-management",
"examples/fetch",
"examples/guide-supported-types-examples",
"examples/hello_world",
Expand Down
22 changes: 22 additions & 0 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,16 @@ impl<'a> Context<'a> {
wasm_bindgen_shared::free_function(name),
));
ts_dst.push_str(" free(): void;\n");
if self.config.symbol_dispose {
dst.push_str(
"
[Symbol.dispose]() {{
this.free();
}}
",
);
ts_dst.push_str(" [Symbol.dispose](): void;\n");
}
dst.push_str(&class.contents);
ts_dst.push_str(&class.typescript);

Expand Down Expand Up @@ -1486,6 +1496,14 @@ impl<'a> Context<'a> {
Ok(ret)
}

fn expose_symbol_dispose(&mut self) -> Result<(), Error> {
if !self.should_write_global("symbol_dispose") {
return Ok(());
}
self.global("if(!Symbol.dispose) { Symbol.dispose = Symbol('Symbol.dispose'); }");
Ok(())
}

fn expose_text_encoder(&mut self) -> Result<(), Error> {
if !self.should_write_global("text_encoder") {
return Ok(());
Expand Down Expand Up @@ -2476,6 +2494,10 @@ impl<'a> Context<'a> {

pub fn generate(&mut self) -> Result<(), Error> {
self.prestore_global_import_identifiers()?;
// conditionally override Symbol.dispose
if self.config.symbol_dispose && !self.aux.structs.is_empty() {
self.expose_symbol_dispose()?;
}
for (id, adapter) in crate::sorted_iter(&self.wit.adapters) {
let instrs = match &adapter.kind {
AdapterKind::Import { .. } => continue,
Expand Down
3 changes: 3 additions & 0 deletions crates/cli-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub struct Bindgen {
multi_value: bool,
encode_into: EncodeInto,
split_linked_modules: bool,
symbol_dispose: bool,
}

pub struct Output {
Expand Down Expand Up @@ -88,6 +89,7 @@ impl Bindgen {
let externref =
env::var("WASM_BINDGEN_ANYREF").is_ok() || env::var("WASM_BINDGEN_EXTERNREF").is_ok();
let multi_value = env::var("WASM_BINDGEN_MULTI_VALUE").is_ok();
let symbol_dispose = env::var("WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE").is_ok();
Bindgen {
input: Input::None,
out_name: None,
Expand All @@ -109,6 +111,7 @@ impl Bindgen {
encode_into: EncodeInto::Test,
omit_default_module_path: true,
split_linked_modules: false,
symbol_dispose,
}
}

Expand Down
12 changes: 12 additions & 0 deletions examples/explicit-resource-management/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
authors = ["The wasm-bindgen Developers"]
edition = "2021"
name = "explicit-resource-management"
publish = false
version = "0.0.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = { path = "../../" }
16 changes: 16 additions & 0 deletions examples/explicit-resource-management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Using Explicit Resource Management (via Deno)

You can build the example with

```sh
$ ./build.sh
```

and test it with

```sh
$ deno run --allow-read test.ts
```

The `--allow-read` flag is needed because the Wasm file is read during runtime.
This will be fixed when https://github.com/denoland/deno/issues/2552 is resolved.
7 changes: 7 additions & 0 deletions examples/explicit-resource-management/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

set -eux

cargo build --target wasm32-unknown-unknown --release
WASM_BINDGEN_EXPERIMENTAL_SYMBOL_DISPOSE=1 cargo run --package wasm-bindgen-cli --bin wasm-bindgen -- \
--out-dir pkg --target deno ${CARGO_TARGET_DIR:-../../target}/wasm32-unknown-unknown/release/explicit_resource_management.wasm
1 change: 1 addition & 0 deletions examples/explicit-resource-management/crate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/explicit-resource-management.wasm
63 changes: 63 additions & 0 deletions examples/explicit-resource-management/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use wasm_bindgen::prelude::*;

// simple counting allocator tracking
struct Counter;

static ALLOCATED: AtomicUsize = AtomicUsize::new(0);

unsafe impl GlobalAlloc for Counter {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ret = System.alloc(layout);
if !ret.is_null() {
ALLOCATED.fetch_add(layout.size(), Relaxed);
}
ret
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout);
ALLOCATED.fetch_sub(layout.size(), Relaxed);
}
}

#[global_allocator]
static A: Counter = Counter;

#[wasm_bindgen]
pub fn current_allocation() -> usize {
ALLOCATED.load(Relaxed)
}

// lifted from the `console_log` example
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}

#[wasm_bindgen]
pub struct MyStruct {
x: Vec<i64>,
y: Vec<i64>,
name: String,
}

#[wasm_bindgen]
impl MyStruct {
#[wasm_bindgen(constructor)]
pub fn new(name: String) -> MyStruct {
Self {
name,
x: (0..50).collect(),
y: (0..50).collect(),
}
}
}

impl Drop for MyStruct {
fn drop(&mut self) {
log(&format!("Goodbye from {}!", self.name)); // should output "Goodbye from Rust!"
}
}
17 changes: 17 additions & 0 deletions examples/explicit-resource-management/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { current_allocation, MyStruct } from "./pkg/explicit_resource_management.js";

const initialAllocation = current_allocation();
let referrent = {};
console.log('Before scope: ', initialAllocation);

{
using foo = new MyStruct("Rust");
// force foo to be treated as live by implicit memory management (FinalizationRegistry/GC)
// by retaining a reference that outlives the scope block (for the purposes of proving
// Symbol.dispose is called on scope exit).
referrent['foo'] = foo;
console.log('After construction, but before scope exit: ', current_allocation());
}
const afterDisposeAllocation = current_allocation();
console.log('After scope exit: ', afterDisposeAllocation);
console.log(referrent);

0 comments on commit 9b81f4b

Please sign in to comment.