From f82adffe23d4bc1c087f318b02a1ffc8dede7c42 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 13 May 2026 18:14:56 -0700 Subject: [PATCH] Adopt wasm-bindgen unwind safety enforcement Update wasm-bindgen submodule to pick up unwind safety enforcement on #[wasm_bindgen] exports (wasm-bindgen/wasm-bindgen#5128). Add wasm-streams and gloo as submodules pointing at forks that propagate unwind safety bounds through closures and timers. Patch both via [patch.crates-io] so the worker crate compiles against the new enforcement. Refactor durable object test fixtures to use Cell wrapped in AssertUnwindSafe, replacing RefCell. Cell is single-threaded safe for Copy types because every operation is an atomic move with no borrow guard to leave dangling; AssertUnwindSafe is the narrow, honest assertion for the interior mutability fields without making blanket promises about State/Env. Add cargo url replacement so transitive git deps fetched via SSH fall back to HTTPS for cargo generate users. --- .cargo/config.toml | 3 +++ .gitmodules | 11 ++++++++--- Cargo.lock | 2 -- Cargo.toml | 3 +++ gloo | 1 + test/src/counter.rs | 32 +++++++++++++++++--------------- test/src/durable.rs | 23 +++++++++++++---------- wasm-bindgen | 2 +- wasm-streams | 1 + 9 files changed, 47 insertions(+), 31 deletions(-) create mode 160000 gloo create mode 160000 wasm-streams diff --git a/.cargo/config.toml b/.cargo/config.toml index 0e465b2de..1acac3562 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,5 @@ [target.wasm32-unknown-unknown] rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] + +[url."https://github.com/"] +insteadOf = ["git@github.com:", "ssh://git@github.com/"] diff --git a/.gitmodules b/.gitmodules index 04cd0236b..e9be48813 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,14 @@ [submodule "wasm-bindgen"] path = wasm-bindgen url = https://github.com/wasm-bindgen/wasm-bindgen -[submodule "wasm-streams"] - path = wasm-streams - url = https://github.com/guybedford/wasm-streams [submodule "ts-gen"] path = ts-gen url = https://github.com/wasm-bindgen/ts-gen +[submodule "wasm-streams"] + path = wasm-streams + url = https://github.com/guybedford/wasm-streams + branch = fix-unwind-safety-impls +[submodule "gloo"] + path = gloo + url = https://github.com/guybedford/gloo + branch = fix-gloo-timers-unwind-safety diff --git a/Cargo.lock b/Cargo.lock index fdc893e34..a7ff8d799 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3540,8 +3540,6 @@ dependencies = [ [[package]] name = "wasm-streams" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 153909cf2..46d8bda84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ exclude = [ "examples/axum", "templates/*", "wasm-bindgen", + "wasm-streams", + "gloo", "generated", ] resolver = "2" @@ -113,3 +115,4 @@ wasm-bindgen-macro-support = { version = "0.2.121", path = "./wasm-bindgen/crate wasm-bindgen-shared = { version = "0.2.121", path = "./wasm-bindgen/crates/shared" } wasm-bindgen-test = { version = "0.3.71", path = "./wasm-bindgen/crates/test" } web-sys = { version = "0.3.98", path = './wasm-bindgen/crates/web-sys' } +wasm-streams = { version = "0.5.0", path = './wasm-streams' } diff --git a/gloo b/gloo new file mode 160000 index 000000000..d73276ed3 --- /dev/null +++ b/gloo @@ -0,0 +1 @@ +Subproject commit d73276ed358882349d5edfb26c97573653dc389c diff --git a/test/src/counter.rs b/test/src/counter.rs index c301efabb..25a19fdb2 100644 --- a/test/src/counter.rs +++ b/test/src/counter.rs @@ -1,4 +1,5 @@ -use std::cell::RefCell; +use std::cell::Cell; +use std::panic::AssertUnwindSafe; use tokio_stream::{StreamExt, StreamMap}; use worker::{ durable_object, wasm_bindgen, wasm_bindgen_futures, DurableObject, Env, Error, Method, Request, @@ -10,28 +11,29 @@ use crate::SomeSharedData; #[durable_object] pub struct Counter { - count: RefCell, - unstored_count: RefCell, + count: AssertUnwindSafe>, + unstored_count: AssertUnwindSafe>, state: State, - initialized: RefCell, + initialized: AssertUnwindSafe>, env: Env, } impl DurableObject for Counter { fn new(state: State, env: Env) -> Self { Self { - count: RefCell::new(0), - unstored_count: RefCell::new(0), - initialized: RefCell::new(false), + count: AssertUnwindSafe(Cell::new(0)), + unstored_count: AssertUnwindSafe(Cell::new(0)), + initialized: AssertUnwindSafe(Cell::new(false)), state, env, } } async fn fetch(&self, req: Request) -> Result { - if !*self.initialized.borrow() { - *self.initialized.borrow_mut() = true; - *self.count.borrow_mut() = self.state.storage().get("count").await?.unwrap_or(0); + if !self.initialized.get() { + self.initialized.set(true); + self.count + .set(self.state.storage().get("count").await?.unwrap_or(0)); } if req.path().eq("/ws") { @@ -49,15 +51,15 @@ impl DurableObject for Counter { .empty()); } - *self.unstored_count.borrow_mut() += 1; - *self.count.borrow_mut() += 10; - let count = *self.count.borrow(); + self.unstored_count.set(self.unstored_count.get() + 1); + self.count.set(self.count.get() + 10); + let count = self.count.get(); self.state.storage().put("count", count).await?; Response::ok(format!( "[durable_object]: self.count: {}, self.unstored_count: {}, secret value: {}", - self.count.borrow(), - self.unstored_count.borrow(), + self.count.get(), + self.unstored_count.get(), self.env.secret("SOME_SECRET")? )) } diff --git a/test/src/durable.rs b/test/src/durable.rs index 832154970..6ec870254 100644 --- a/test/src/durable.rs +++ b/test/src/durable.rs @@ -1,6 +1,8 @@ use serde::Serialize; +use std::cell::Cell; +use std::collections::HashMap; use std::convert::TryFrom; -use std::{cell::RefCell, collections::HashMap}; +use std::panic::AssertUnwindSafe; use worker::DurableObject; use worker::{ @@ -13,14 +15,14 @@ use worker::{ #[durable_object] pub struct MyClass { state: State, - number: RefCell, + number: AssertUnwindSafe>, } impl DurableObject for MyClass { fn new(state: State, _env: Env) -> Self { Self { state, - number: RefCell::new(0), + number: AssertUnwindSafe(Cell::new(0)), } } @@ -153,13 +155,14 @@ impl DurableObject for MyClass { ); } - *self.number.borrow_mut() = storage.get("count").await?.unwrap_or(0) + 1; + self.number + .set(storage.get("count").await?.unwrap_or(0) + 1); storage.delete_all().await?; - let count = *self.number.borrow(); + let count = self.number.get(); storage.put("count", count).await?; - Response::ok(self.number.borrow().to_string()) + Response::ok(self.number.get().to_string()) } "/transaction" => { Response::error("transactional storage API is still unstable", 501) @@ -178,20 +181,20 @@ impl DurableObject for MyClass { pub struct AnotherClass { #[allow(unused)] state: State, - counter: RefCell, + counter: AssertUnwindSafe>, } impl DurableObject for AnotherClass { fn new(state: State, _env: Env) -> Self { Self { state, - counter: RefCell::new(0), + counter: AssertUnwindSafe(Cell::new(0)), } } async fn fetch(&self, _req: Request) -> Result { - *self.counter.borrow_mut() += 1; - Response::ok(format!("Counter: {}", self.counter.borrow())) + self.counter.set(self.counter.get() + 1); + Response::ok(format!("Counter: {}", self.counter.get())) } } diff --git a/wasm-bindgen b/wasm-bindgen index 49457f2db..8b5d21384 160000 --- a/wasm-bindgen +++ b/wasm-bindgen @@ -1 +1 @@ -Subproject commit 49457f2db4465688cb597e9030ccfdefbd2b662e +Subproject commit 8b5d213843ddbdffd9d23104c469a882aec157b2 diff --git a/wasm-streams b/wasm-streams new file mode 160000 index 000000000..c4035580f --- /dev/null +++ b/wasm-streams @@ -0,0 +1 @@ +Subproject commit c4035580f31a1f3c907d361d8e43ebac6c738a26