From 26781fa322cac6591f6d60c74a3c10c29dc1a37f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 6 Aug 2024 23:29:26 +0200 Subject: [PATCH] Deprecate `JsStatic in favor of `#[wasm_bindgen(thread_local)]` Removed `impl Deref for JsStatic` when compiling with `cfg(target_feature = "atomics")`, which was unsound. --- CHANGELOG.md | 8 +- crates/backend/src/ast.rs | 2 + crates/backend/src/codegen.rs | 95 ++++++++++++------- crates/js-sys/tests/wasm/Function.rs | 36 ++++--- crates/js-sys/tests/wasm/Object.rs | 16 ++-- crates/macro-support/src/parser.rs | 20 +++- crates/macro/Cargo.toml | 1 + crates/macro/ui-tests/invalid-items.rs | 15 ++- crates/macro/ui-tests/invalid-items.stderr | 52 +++++++--- .../macro/ui-tests/invalid-static-string.rs | 10 ++ .../ui-tests/invalid-static-string.stderr | 14 +++ crates/test/src/rt/browser.rs | 5 +- crates/web-sys/tests/wasm/history.rs | 32 ++++--- crates/web-sys/tests/wasm/performance.rs | 4 +- .../src/dependent_module.rs | 4 +- .../indexing-getter-setter-deleter.md | 15 +-- guide/src/reference/static-js-objects.md | 9 +- src/lib.rs | 11 +-- tests/headless/strings.rs | 4 +- tests/wasm/duplicates.rs | 20 ++-- tests/wasm/imports.rs | 14 +-- tests/wasm/node.rs | 3 +- 22 files changed, 264 insertions(+), 126 deletions(-) create mode 100644 crates/macro/ui-tests/invalid-static-string.rs create mode 100644 crates/macro/ui-tests/invalid-static-string.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e9f9997dd695..df7ad8a62177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ * Added an experimental Node.JS ES module target, in comparison the current `node` target uses CommonJS, with `--target experimental-nodejs-module` or when testing with `wasm_bindgen_test_configure!(run_in_node_experimental)`. [#4027](https://github.com/rustwasm/wasm-bindgen/pull/4027) -* Added importing strings as `JsString` through `#[wasm_bindgen(static_string)] static STRING: JsString = "a string literal";`. +* Added importing strings as `JsString` through `#[wasm_bindgen(thread_local, static_string)] static STRING: JsString = "a string literal";`. [#4055](https://github.com/rustwasm/wasm-bindgen/pull/4055) ### Changed @@ -129,6 +129,12 @@ * Filtered files in published crates, significantly reducing the package size and notably excluding any bash files. [#4046](https://github.com/rustwasm/wasm-bindgen/pull/4046) +* Deprecated `JsStatic` in favor of `#[wasm_bindgen(thread_local)]`, which creates a `std::thread::LocalKey`. The syntax is otherwise the same. + [#4057](https://github.com/rustwasm/wasm-bindgen/pull/4057) + +* Removed `impl Deref for JsStatic` when compiling with `cfg(target_feature = "atomics")`, which was unsound. + [#4057](https://github.com/rustwasm/wasm-bindgen/pull/4057) + ### Fixed * Copy port from headless test server when using `WASM_BINDGEN_TEST_ADDRESS`. diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 3474d3a5a51f..e10cf756ddda 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -275,6 +275,8 @@ pub struct ImportStatic { pub js_name: String, /// Path to wasm_bindgen pub wasm_bindgen: Path, + /// [`true`] if using the new `thread_local` representation. + pub thread_local: bool, } /// The type of a static string being imported diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index c826d67cb4d5..4add9859cd6a 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1645,15 +1645,42 @@ impl ToTokens for ast::Enum { impl ToTokens for ast::ImportStatic { fn to_tokens(&self, into: &mut TokenStream) { let ty = &self.ty; - static_import( - &self.vis, - &self.rust_name, - &self.wasm_bindgen, - ty, - ty, - &self.shim, - ) - .to_tokens(into); + + if self.thread_local { + thread_local_import( + &self.vis, + &self.rust_name, + &self.wasm_bindgen, + ty, + ty, + &self.shim, + ) + .to_tokens(into) + } else { + let vis = &self.vis; + let name = &self.rust_name; + let wasm_bindgen = &self.wasm_bindgen; + let ty = &self.ty; + let shim_name = &self.shim; + let init = static_init(wasm_bindgen, ty, shim_name); + + into.extend(quote! { + #[automatically_derived] + #[deprecated = "use with `#[wasm_bindgen(thread_local)]` instead"] + }); + into.extend( + quote_spanned! { name.span() => #vis static #name: #wasm_bindgen::JsStatic<#ty> = { + fn init() -> #ty { + #init + } + thread_local!(static _VAL: #ty = init();); + #wasm_bindgen::JsStatic { + __inner: &_VAL, + } + }; + }, + ); + } Descriptor { ident: &self.shim, @@ -1672,7 +1699,7 @@ impl ToTokens for ast::ImportString { let js_sys = &self.js_sys; let actual_ty: syn::Type = parse_quote!(#js_sys::JsString); - static_import( + thread_local_import( &self.vis, &self.rust_name, &self.wasm_bindgen, @@ -1684,7 +1711,7 @@ impl ToTokens for ast::ImportString { } } -fn static_import( +fn thread_local_import( vis: &syn::Visibility, name: &Ident, wasm_bindgen: &syn::Path, @@ -1692,33 +1719,37 @@ fn static_import( ty: &syn::Type, shim_name: &Ident, ) -> TokenStream { + let init = static_init(wasm_bindgen, ty, shim_name); + + quote! { + thread_local! { + #[automatically_derived] + #vis static #name: #actual_ty = { + #init + }; + } + } +} + +fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> TokenStream { let abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; quote! { - #[automatically_derived] - #vis static #name: #wasm_bindgen::JsStatic<#actual_ty> = { - fn init() -> #ty { - #[link(wasm_import_module = "__wbindgen_placeholder__")] - #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] - extern "C" { - fn #shim_name() -> #abi_ret; - } + #[link(wasm_import_module = "__wbindgen_placeholder__")] + #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] + extern "C" { + fn #shim_name() -> #abi_ret; + } - #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] - unsafe fn #shim_name() -> #abi_ret { - panic!("cannot access imported statics on non-wasm targets") - } + #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] + unsafe fn #shim_name() -> #abi_ret { + panic!("cannot access imported statics on non-wasm targets") + } - unsafe { - <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join()) - } - } - thread_local!(static _VAL: #ty = init();); - #wasm_bindgen::JsStatic { - __inner: &_VAL, - } - }; + unsafe { + <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join()) + } } } diff --git a/crates/js-sys/tests/wasm/Function.rs b/crates/js-sys/tests/wasm/Function.rs index c87a46cca2d6..f0f27dca123d 100644 --- a/crates/js-sys/tests/wasm/Function.rs +++ b/crates/js-sys/tests/wasm/Function.rs @@ -5,13 +5,13 @@ use wasm_bindgen_test::*; #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = max, js_namespace = Math)] + #[wasm_bindgen(thread_local, js_name = max, js_namespace = Math)] static MAX: Function; type ArrayPrototype; #[wasm_bindgen(method, getter, structural)] pub fn push(this: &ArrayPrototype) -> Function; - #[wasm_bindgen(js_name = prototype, js_namespace = Array)] + #[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Array)] static ARRAY_PROTOTYPE2: ArrayPrototype; } @@ -21,12 +21,19 @@ fn apply() { args.push(&1.into()); args.push(&2.into()); args.push(&3.into()); - assert_eq!(MAX.apply(&JsValue::undefined(), &args).unwrap(), 3); + assert_eq!( + MAX.with(|max| max.apply(&JsValue::undefined(), &args)) + .unwrap(), + 3 + ); let arr = JsValue::from(Array::new()); let args = Array::new(); args.push(&1.into()); - ARRAY_PROTOTYPE2.push().apply(&arr, &args).unwrap(); + ARRAY_PROTOTYPE2 + .with(ArrayPrototype::push) + .apply(&arr, &args) + .unwrap(); assert_eq!(Array::from(&arr).length(), 1); } @@ -111,24 +118,29 @@ fn bind3() { #[wasm_bindgen_test] fn length() { - assert_eq!(MAX.length(), 2); - assert_eq!(ARRAY_PROTOTYPE2.push().length(), 1); + assert_eq!(MAX.with(Function::length), 2); + assert_eq!(ARRAY_PROTOTYPE2.with(ArrayPrototype::push).length(), 1); } #[wasm_bindgen_test] fn name() { - assert_eq!(JsValue::from(MAX.name()), "max"); - assert_eq!(JsValue::from(ARRAY_PROTOTYPE2.push().name()), "push"); + assert_eq!(JsValue::from(MAX.with(Function::name)), "max"); + assert_eq!( + JsValue::from(ARRAY_PROTOTYPE2.with(ArrayPrototype::push).name()), + "push" + ); } #[wasm_bindgen_test] fn to_string() { - assert!(MAX.to_string().length() > 0); + assert!(MAX.with(Function::to_string).length() > 0); } #[wasm_bindgen_test] fn function_inheritance() { - assert!(MAX.is_instance_of::()); - assert!(MAX.is_instance_of::()); - let _: &Object = MAX.as_ref(); + assert!(MAX.with(Function::is_instance_of::)); + assert!(MAX.with(Function::is_instance_of::)); + MAX.with(|max| { + let _: &Object = max.as_ref(); + }); } diff --git a/crates/js-sys/tests/wasm/Object.rs b/crates/js-sys/tests/wasm/Object.rs index 69db1af3af56..b97c31d0fbfe 100644 --- a/crates/js-sys/tests/wasm/Object.rs +++ b/crates/js-sys/tests/wasm/Object.rs @@ -9,9 +9,9 @@ extern "C" { #[wasm_bindgen(method, setter, structural)] fn set_foo(this: &Foo42, val: JsValue); - #[wasm_bindgen(js_name = prototype, js_namespace = Object)] + #[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Object)] static OBJECT_PROTOTYPE: JsValue; - #[wasm_bindgen(js_name = prototype, js_namespace = Array)] + #[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Array)] static ARRAY_PROTOTYPE: JsValue; type DefinePropertyAttrs; @@ -32,9 +32,9 @@ extern "C" { #[wasm_bindgen(constructor)] fn new() -> Foo; - #[wasm_bindgen(js_name = prototype, js_namespace = Foo)] + #[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Foo)] static FOO_PROTOTYPE: Object; - #[wasm_bindgen(js_name = prototype, js_namespace = Bar)] + #[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Bar)] static BAR_PROTOTYPE: Object; } @@ -178,9 +178,9 @@ fn get_own_property_symbols() { #[wasm_bindgen_test] fn get_prototype_of() { let proto = JsValue::from(Object::get_prototype_of(&Object::new().into())); - assert_eq!(proto, *OBJECT_PROTOTYPE); + OBJECT_PROTOTYPE.with(|op| assert_eq!(proto, *op)); let proto = JsValue::from(Object::get_prototype_of(&Array::new().into())); - assert_eq!(proto, *ARRAY_PROTOTYPE); + ARRAY_PROTOTYPE.with(|ap| assert_eq!(proto, *ap)); } #[wasm_bindgen_test] @@ -249,8 +249,8 @@ fn is_sealed() { #[wasm_bindgen_test] fn is_prototype_of() { let foo = JsValue::from(Foo::new()); - assert!(FOO_PROTOTYPE.is_prototype_of(&foo)); - assert!(!BAR_PROTOTYPE.is_prototype_of(&foo)); + assert!(FOO_PROTOTYPE.with(|fp| fp.is_prototype_of(&foo))); + assert!(!BAR_PROTOTYPE.with(|bp| bp.is_prototype_of(&foo))); } #[wasm_bindgen_test] diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 3e3f3113a0e1..bcc4d73932f2 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -93,6 +93,7 @@ macro_rules! attrgen { (typescript_type, TypeScriptType(Span, String, Span)), (getter_with_clone, GetterWithClone(Span)), (static_string, StaticString(Span)), + (thread_local, ThreadLocal(Span)), // For testing purposes only. (assert_no_shim, AssertNoShim(Span)), @@ -753,6 +754,13 @@ impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option(T); #[wasm_bindgen] +#[rustfmt::skip] extern "C" { static mut FOO: u32; + #[wasm_bindgen(static_string)] + static FOO2: JsString; + + #[wasm_bindgen(thread_local, static_string)] + static FOO3: JsString; + + static FOO4: JsString = "test"; + + #[wasm_bindgen(static_string)] + static FOO5: JsString = "test"; + pub fn foo3(x: i32, ...); } #[wasm_bindgen] -extern "system" { -} +extern "system" {} #[wasm_bindgen] pub fn foo4() {} diff --git a/crates/macro/ui-tests/invalid-items.stderr b/crates/macro/ui-tests/invalid-items.stderr index 1800ade29d70..f985c96c0701 100644 --- a/crates/macro/ui-tests/invalid-items.stderr +++ b/crates/macro/ui-tests/invalid-items.stderr @@ -17,43 +17,67 @@ error: structs with #[wasm_bindgen] cannot have lifetime or type parameters curr | ^^^ error: cannot import mutable globals yet - --> $DIR/invalid-items.rs:14:12 + --> $DIR/invalid-items.rs:15:12 | -14 | static mut FOO: u32; +15 | static mut FOO: u32; | ^^^ +error: static strings require a string literal + --> $DIR/invalid-items.rs:17:20 + | +17 | #[wasm_bindgen(static_string)] + | ^^^^^^^^^^^^^ + +error: static strings require a string literal + --> $DIR/invalid-items.rs:20:34 + | +20 | #[wasm_bindgen(thread_local, static_string)] + | ^^^^^^^^^^^^^ + +error: static strings require `#[wasm_bindgen(static_string)]` + --> $DIR/invalid-items.rs:23:5 + | +23 | static FOO4: JsString = "test"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: static strings require `#[wasm_bindgen(thread_local)]` + --> $DIR/invalid-items.rs:26:5 + | +26 | static FOO5: JsString = "test"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: can't #[wasm_bindgen] variadic functions - --> $DIR/invalid-items.rs:16:25 + --> $DIR/invalid-items.rs:28:25 | -16 | pub fn foo3(x: i32, ...); +28 | pub fn foo3(x: i32, ...); | ^^^ error: only foreign mods with the `C` ABI are allowed - --> $DIR/invalid-items.rs:20:8 + --> $DIR/invalid-items.rs:32:8 | -20 | extern "system" { +32 | extern "system" {} | ^^^^^^^^ error: can't #[wasm_bindgen] functions with lifetime or type parameters - --> $DIR/invalid-items.rs:24:12 + --> $DIR/invalid-items.rs:35:12 | -24 | pub fn foo4() {} +35 | pub fn foo4() {} | ^^^ error: can't #[wasm_bindgen] functions with lifetime or type parameters - --> $DIR/invalid-items.rs:26:12 + --> $DIR/invalid-items.rs:37:12 | -26 | pub fn foo5<'a>() {} +37 | pub fn foo5<'a>() {} | ^^^^ error: can't #[wasm_bindgen] functions with lifetime or type parameters - --> $DIR/invalid-items.rs:28:12 + --> $DIR/invalid-items.rs:39:12 | -28 | pub fn foo6<'a, T>() {} +39 | pub fn foo6<'a, T>() {} | ^^^^^^^ error: #[wasm_bindgen] can only be applied to a function, struct, enum, impl, or extern block - --> $DIR/invalid-items.rs:31:1 + --> $DIR/invalid-items.rs:42:1 | -31 | trait X {} +42 | trait X {} | ^^^^^^^^^^ diff --git a/crates/macro/ui-tests/invalid-static-string.rs b/crates/macro/ui-tests/invalid-static-string.rs new file mode 100644 index 000000000000..7b50b1079b03 --- /dev/null +++ b/crates/macro/ui-tests/invalid-static-string.rs @@ -0,0 +1,10 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[rustfmt::skip] +extern "C" { + #[wasm_bindgen(thread_local, static_string)] + static FOO: JsValue = "test"; +} + +fn main() {} diff --git a/crates/macro/ui-tests/invalid-static-string.stderr b/crates/macro/ui-tests/invalid-static-string.stderr new file mode 100644 index 000000000000..7d1872a10ec2 --- /dev/null +++ b/crates/macro/ui-tests/invalid-static-string.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/invalid-static-string.rs:3:1 + | +3 | #[wasm_bindgen] + | ^^^^^^^^^^^^^^^ + | | + | expected `JsString`, found `JsValue` + | expected `JsString` because of return type + | + = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) +help: call `Into::into` on this expression to convert `wasm_bindgen::JsValue` into `JsString` + | +3 | #[wasm_bindgen].into() + | +++++++ diff --git a/crates/test/src/rt/browser.rs b/crates/test/src/rt/browser.rs index fc576f6db303..49b27725e75f 100644 --- a/crates/test/src/rt/browser.rs +++ b/crates/test/src/rt/browser.rs @@ -19,7 +19,8 @@ pub struct Browser { #[wasm_bindgen] extern "C" { type HTMLDocument; - static document: HTMLDocument; + #[wasm_bindgen(thread_local, js_name = document)] + static DOCUMENT: HTMLDocument; #[wasm_bindgen(method, structural)] fn getElementById(this: &HTMLDocument, id: &str) -> Element; @@ -38,7 +39,7 @@ impl Browser { /// Creates a new instance of `Browser`, assuming that its APIs will work /// (requires `Node::new()` to have return `None` first). pub fn new() -> Browser { - let pre = document.getElementById("output"); + let pre = DOCUMENT.with(|document| document.getElementById("output")); pre.set_text_content(""); Browser { pre } } diff --git a/crates/web-sys/tests/wasm/history.rs b/crates/web-sys/tests/wasm/history.rs index f3ecdc42eb03..e2800520eb9f 100644 --- a/crates/web-sys/tests/wasm/history.rs +++ b/crates/web-sys/tests/wasm/history.rs @@ -4,25 +4,27 @@ use web_sys::{History, ScrollRestoration}; #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = history, js_namespace = window)] + #[wasm_bindgen(thread_local, js_name = history, js_namespace = window)] static HISTORY: History; } #[wasm_bindgen_test] fn history() { - HISTORY - .set_scroll_restoration(ScrollRestoration::Manual) - .expect("failure to set scroll restoration"); - assert_eq!( - HISTORY.scroll_restoration().unwrap(), - ScrollRestoration::Manual - ); + HISTORY.with(|history| { + history + .set_scroll_restoration(ScrollRestoration::Manual) + .expect("failure to set scroll restoration"); + assert_eq!( + history.scroll_restoration().unwrap(), + ScrollRestoration::Manual + ); - HISTORY - .set_scroll_restoration(ScrollRestoration::Auto) - .expect("failure to set scroll restoration"); - assert_eq!( - HISTORY.scroll_restoration().unwrap(), - ScrollRestoration::Auto - ); + history + .set_scroll_restoration(ScrollRestoration::Auto) + .expect("failure to set scroll restoration"); + assert_eq!( + history.scroll_restoration().unwrap(), + ScrollRestoration::Auto + ); + }); } diff --git a/crates/web-sys/tests/wasm/performance.rs b/crates/web-sys/tests/wasm/performance.rs index 84931a627cd8..09fb59a66a1d 100644 --- a/crates/web-sys/tests/wasm/performance.rs +++ b/crates/web-sys/tests/wasm/performance.rs @@ -4,12 +4,12 @@ use web_sys::Performance; #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = performance)] + #[wasm_bindgen(thread_local, js_name = performance)] static PERFORMANCE: Performance; } #[wasm_bindgen_test] fn to_json() { - let perf = JsValue::from(PERFORMANCE.to_json()); + let perf = JsValue::from(PERFORMANCE.with(Performance::to_json)); assert!(perf.is_object()); } diff --git a/examples/wasm-audio-worklet/src/dependent_module.rs b/examples/wasm-audio-worklet/src/dependent_module.rs index ce7f038fdcd2..5e074d72b0be 100644 --- a/examples/wasm-audio-worklet/src/dependent_module.rs +++ b/examples/wasm-audio-worklet/src/dependent_module.rs @@ -12,7 +12,7 @@ extern "C" { #[wasm_bindgen(method, getter)] fn url(this: &ImportMeta) -> JsString; - #[wasm_bindgen(js_namespace = import, js_name = meta)] + #[wasm_bindgen(thread_local, js_namespace = import, js_name = meta)] static IMPORT_META: ImportMeta; } @@ -20,7 +20,7 @@ pub fn on_the_fly(code: &str) -> Result { // Generate the import of the bindgen ES module, assuming `--target web`. let header = format!( "import init, * as bindgen from '{}';\n\n", - IMPORT_META.url(), + IMPORT_META.with(ImportMeta::url), ); let options = BlobPropertyBag::new(); diff --git a/guide/src/reference/attributes/on-js-imports/indexing-getter-setter-deleter.md b/guide/src/reference/attributes/on-js-imports/indexing-getter-setter-deleter.md index 5dad7d461a70..0d0443cb001e 100644 --- a/guide/src/reference/attributes/on-js-imports/indexing-getter-setter-deleter.md +++ b/guide/src/reference/attributes/on-js-imports/indexing-getter-setter-deleter.md @@ -60,7 +60,8 @@ on methods: #[wasm_bindgen] extern "C" { type Foo; - static foo: Foo; + #[wasm_bindgen(thread_local)] + static FOO: Foo; #[wasm_bindgen(method, structural, indexing_getter)] fn get(this: &Foo, prop: &str) -> u32; @@ -72,11 +73,13 @@ extern "C" { fn delete(this: &Foo, prop: &str); } -assert_eq!(foo.get("ten"), 3); +FOO.with(|foo| { + assert_eq!(foo.get("ten"), 3); -foo.set("ten", 10); -assert_eq!(foo.get("ten"), 10); + foo.set("ten", 10); + assert_eq!(foo.get("ten"), 10); -foo.delete("ten"); -assert_eq!(foo.get("ten"), 3); + foo.delete("ten"); + assert_eq!(foo.get("ten"), 3); +}); ``` diff --git a/guide/src/reference/static-js-objects.md b/guide/src/reference/static-js-objects.md index 9c15fcbb3d5b..2402098e6068 100644 --- a/guide/src/reference/static-js-objects.md +++ b/guide/src/reference/static-js-objects.md @@ -19,11 +19,12 @@ let COLORS = { ```rust #[wasm_bindgen] extern "C" { + #[wasm_bindgen(thread_local)] static COLORS; } fn get_colors() -> JsValue { - COLORS.clone() + COLORS.with(JsValue::clone) } ``` @@ -49,11 +50,11 @@ The binding for this module: #[wasm_bindgen(module = "/js/some-rollup.js")] extern "C" { // Likewise with the namespace--this refers to the object directly. - #[wasm_bindgen(js_name = namespace)] + #[wasm_bindgen(thread_local, js_name = namespace)] static NAMESPACE: JsValue; // Refer to SomeType's class - #[wasm_bindgen(js_name = SomeType)] + #[wasm_bindgen(thread_local, js_name = SomeType)] static SOME_TYPE: JsValue; // Other bindings for SomeType @@ -70,7 +71,7 @@ Strings can be imported to avoid going through `TextDecoder/Encoder` when requir ```rust #[wasm_bindgen] extern "C" { - #[wasm_bindgen(static_string)] + #[wasm_bindgen(thread_local, static_string)] static STRING: JsString = "a string literal"; } ``` diff --git a/src/lib.rs b/src/lib.rs index a691426994c4..e8e30b9aa958 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1185,23 +1185,18 @@ impl Default for JsValue { /// This type implements `Deref` to the inner type so it's typically used as if /// it were `&T`. #[cfg(feature = "std")] +#[deprecated = "use with `#[wasm_bindgen(thread_local)]` instead"] pub struct JsStatic { #[doc(hidden)] pub __inner: &'static std::thread::LocalKey, } #[cfg(feature = "std")] +#[allow(deprecated)] +#[cfg(not(target_feature = "atomics"))] impl Deref for JsStatic { type Target = T; fn deref(&self) -> &T { - // We know that our tls key is never overwritten after initialization, - // so it should be safe (on that axis at least) to hand out a reference - // that lives longer than the closure below. - // - // FIXME: this is not sound if we ever implement thread exit hooks on - // wasm, as the pointer will eventually be invalidated but you can get - // `&'static T` from this interface. We... probably need to deprecate - // and/or remove this interface nowadays. unsafe { self.__inner.with(|ptr| &*(ptr as *const T)) } } } diff --git a/tests/headless/strings.rs b/tests/headless/strings.rs index 944db5f2e565..baa551b89505 100644 --- a/tests/headless/strings.rs +++ b/tests/headless/strings.rs @@ -15,7 +15,7 @@ fn string_roundtrip() { assert_eq!("\u{feff}bar", &identity("\u{feff}bar")); - assert_eq!(String::from(&*STRING), "foo") + assert_eq!(STRING.with(|s| String::from(s)), "foo"); } #[wasm_bindgen] @@ -23,6 +23,6 @@ fn string_roundtrip() { // See . #[rustfmt::skip] extern "C" { - #[wasm_bindgen(static_string)] + #[wasm_bindgen(thread_local, static_string)] static STRING: JsString = "foo"; } diff --git a/tests/wasm/duplicates.rs b/tests/wasm/duplicates.rs index 206fede7183c..348f67b8cd00 100644 --- a/tests/wasm/duplicates.rs +++ b/tests/wasm/duplicates.rs @@ -6,7 +6,8 @@ pub mod same_function_different_locations_a { #[wasm_bindgen(module = "tests/wasm/duplicates_a.js")] extern "C" { pub fn foo(); - pub static bar: JsValue; + #[wasm_bindgen(thread_local, js_name = bar)] + pub static BAR: JsValue; } } @@ -16,7 +17,8 @@ pub mod same_function_different_locations_b { #[wasm_bindgen(module = "tests/wasm/duplicates_a.js")] extern "C" { pub fn foo(); - pub static bar: JsValue; + #[wasm_bindgen(thread_local, js_name = bar)] + pub static BAR: JsValue; } } @@ -24,8 +26,8 @@ pub mod same_function_different_locations_b { fn same_function_different_locations() { same_function_different_locations_a::foo(); same_function_different_locations_b::foo(); - assert_eq!(*same_function_different_locations_a::bar, 3); - assert_eq!(*same_function_different_locations_a::bar, 3); + same_function_different_locations_a::BAR.with(|bar| assert_eq!(*bar, 3)); + same_function_different_locations_a::BAR.with(|bar| assert_eq!(*bar, 3)); } pub mod same_function_different_modules_a { @@ -34,7 +36,8 @@ pub mod same_function_different_modules_a { #[wasm_bindgen(module = "tests/wasm/duplicates_b.js")] extern "C" { pub fn foo() -> bool; - pub static bar: JsValue; + #[wasm_bindgen(thread_local, js_name = bar)] + pub static BAR: JsValue; } } @@ -44,7 +47,8 @@ pub mod same_function_different_modules_b { #[wasm_bindgen(module = "tests/wasm/duplicates_c.js")] extern "C" { pub fn foo() -> bool; - pub static bar: JsValue; + #[wasm_bindgen(thread_local, js_name = bar)] + pub static BAR: JsValue; } } @@ -52,6 +56,6 @@ pub mod same_function_different_modules_b { fn same_function_different_modules() { assert!(same_function_different_modules_a::foo()); assert!(!same_function_different_modules_b::foo()); - assert_eq!(*same_function_different_modules_a::bar, 4); - assert_eq!(*same_function_different_modules_b::bar, 5); + same_function_different_modules_a::BAR.with(|bar| assert_eq!(*bar, 4)); + same_function_different_modules_b::BAR.with(|bar| assert_eq!(*bar, 5)); } diff --git a/tests/wasm/imports.rs b/tests/wasm/imports.rs index a9fbb33b7b1c..aa5a8b4f0384 100644 --- a/tests/wasm/imports.rs +++ b/tests/wasm/imports.rs @@ -23,6 +23,7 @@ extern "C" { fn assert_valid_error(val: JsValue); + #[wasm_bindgen(thread_local)] static IMPORT: JsValue; #[wasm_bindgen(js_name = return_three)] @@ -35,7 +36,7 @@ extern "C" { #[allow(non_camel_case_types)] type bar; - #[wasm_bindgen(js_namespace = bar, js_name = foo)] + #[wasm_bindgen(thread_local, js_namespace = bar, js_name = foo)] static FOO: JsValue; fn take_custom_type(f: CustomType) -> CustomType; @@ -46,7 +47,7 @@ extern "C" { #[wasm_bindgen(js_name = "baz$")] fn renamed_with_dollar_sign(); - #[wasm_bindgen(js_name = "$foo")] + #[wasm_bindgen(thread_local, js_name = "$foo")] static RENAMED: JsValue; fn unused_import(); @@ -57,6 +58,7 @@ extern "C" { #[wasm_bindgen(static_method_of = StaticMethodCheck)] fn static_method_of_right_this(); + #[wasm_bindgen(thread_local)] static STATIC_STRING: String; #[derive(Clone)] @@ -170,7 +172,7 @@ fn free_imports() { #[wasm_bindgen_test] fn import_a_field() { - assert_eq!(IMPORT.as_f64(), Some(1.0)); + assert_eq!(IMPORT.with(JsValue::as_f64), Some(1.0)); } #[wasm_bindgen_test] @@ -190,7 +192,7 @@ fn rust_keyword() { #[wasm_bindgen_test] fn rust_keyword2() { - assert_eq!(FOO.as_f64(), Some(3.0)); + assert_eq!(FOO.with(JsValue::as_f64), Some(3.0)); } #[wasm_bindgen_test] @@ -222,7 +224,7 @@ fn rename_with_string() { #[wasm_bindgen_test] fn rename_static_with_string() { - assert_eq!(RENAMED.as_f64(), Some(1.0)); + assert_eq!(RENAMED.with(JsValue::as_f64), Some(1.0)); } #[wasm_bindgen_test] @@ -285,7 +287,7 @@ fn undefined_function_is_ok() { #[wasm_bindgen_test] fn static_string_ok() { - assert_eq!(*STATIC_STRING, "x"); + STATIC_STRING.with(|s| assert_eq!(*s, "x")); } #[wasm_bindgen_test] diff --git a/tests/wasm/node.rs b/tests/wasm/node.rs index 8f57e1731d10..59f43eb71d9f 100644 --- a/tests/wasm/node.rs +++ b/tests/wasm/node.rs @@ -4,6 +4,7 @@ use wasm_bindgen_test::*; #[wasm_bindgen(module = "tests/wasm/node.js")] extern "C" { fn test_works(); + #[wasm_bindgen(thread_local)] static FOO: JsValue; fn hit(); } @@ -11,7 +12,7 @@ extern "C" { #[wasm_bindgen_test] fn works() { hit(); - assert_eq!(FOO.as_f64(), Some(1.0)); + assert_eq!(FOO.with(JsValue::as_f64), Some(1.0)); test_works(); }