Skip to content

Commit acfd1ee

Browse files
authored
Deprecate JsStatic in favor of #[wasm_bindgen(thread_local)] (#4057)
Removed `impl Deref for JsStatic` when compiling with `cfg(target_feature = "atomics")`, which was unsound.
1 parent 0b1cce6 commit acfd1ee

File tree

23 files changed

+266
-127
lines changed

23 files changed

+266
-127
lines changed

CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
* 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)`.
7373
[#4027](https://github.com/rustwasm/wasm-bindgen/pull/4027)
7474

75-
* Added importing strings as `JsString` through `#[wasm_bindgen(static_string)] static STRING: JsString = "a string literal";`.
75+
* Added importing strings as `JsString` through `#[wasm_bindgen(thread_local, static_string)] static STRING: JsString = "a string literal";`.
7676
[#4055](https://github.com/rustwasm/wasm-bindgen/pull/4055)
7777

7878
### Changed
@@ -129,6 +129,12 @@
129129
* Filtered files in published crates, significantly reducing the package size and notably excluding any bash files.
130130
[#4046](https://github.com/rustwasm/wasm-bindgen/pull/4046)
131131

132+
* Deprecated `JsStatic` in favor of `#[wasm_bindgen(thread_local)]`, which creates a `std::thread::LocalKey`. The syntax is otherwise the same.
133+
[#4057](https://github.com/rustwasm/wasm-bindgen/pull/4057)
134+
135+
* Removed `impl Deref for JsStatic` when compiling with `cfg(target_feature = "atomics")`, which was unsound.
136+
[#4057](https://github.com/rustwasm/wasm-bindgen/pull/4057)
137+
132138
### Fixed
133139

134140
* Copy port from headless test server when using `WASM_BINDGEN_TEST_ADDRESS`.

crates/backend/src/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ pub struct ImportStatic {
275275
pub js_name: String,
276276
/// Path to wasm_bindgen
277277
pub wasm_bindgen: Path,
278+
/// [`true`] if using the new `thread_local` representation.
279+
pub thread_local: bool,
278280
}
279281

280282
/// The type of a static string being imported

crates/backend/src/codegen.rs

+63-32
Original file line numberDiff line numberDiff line change
@@ -1645,15 +1645,42 @@ impl ToTokens for ast::Enum {
16451645
impl ToTokens for ast::ImportStatic {
16461646
fn to_tokens(&self, into: &mut TokenStream) {
16471647
let ty = &self.ty;
1648-
static_import(
1649-
&self.vis,
1650-
&self.rust_name,
1651-
&self.wasm_bindgen,
1652-
ty,
1653-
ty,
1654-
&self.shim,
1655-
)
1656-
.to_tokens(into);
1648+
1649+
if self.thread_local {
1650+
thread_local_import(
1651+
&self.vis,
1652+
&self.rust_name,
1653+
&self.wasm_bindgen,
1654+
ty,
1655+
ty,
1656+
&self.shim,
1657+
)
1658+
.to_tokens(into)
1659+
} else {
1660+
let vis = &self.vis;
1661+
let name = &self.rust_name;
1662+
let wasm_bindgen = &self.wasm_bindgen;
1663+
let ty = &self.ty;
1664+
let shim_name = &self.shim;
1665+
let init = static_init(wasm_bindgen, ty, shim_name);
1666+
1667+
into.extend(quote! {
1668+
#[automatically_derived]
1669+
#[deprecated = "use with `#[wasm_bindgen(thread_local)]` instead"]
1670+
});
1671+
into.extend(
1672+
quote_spanned! { name.span() => #vis static #name: #wasm_bindgen::JsStatic<#ty> = {
1673+
fn init() -> #ty {
1674+
#init
1675+
}
1676+
thread_local!(static _VAL: #ty = init(););
1677+
#wasm_bindgen::JsStatic {
1678+
__inner: &_VAL,
1679+
}
1680+
};
1681+
},
1682+
);
1683+
}
16571684

16581685
Descriptor {
16591686
ident: &self.shim,
@@ -1672,7 +1699,7 @@ impl ToTokens for ast::ImportString {
16721699
let js_sys = &self.js_sys;
16731700
let actual_ty: syn::Type = parse_quote!(#js_sys::JsString);
16741701

1675-
static_import(
1702+
thread_local_import(
16761703
&self.vis,
16771704
&self.rust_name,
16781705
&self.wasm_bindgen,
@@ -1684,41 +1711,45 @@ impl ToTokens for ast::ImportString {
16841711
}
16851712
}
16861713

1687-
fn static_import(
1714+
fn thread_local_import(
16881715
vis: &syn::Visibility,
16891716
name: &Ident,
16901717
wasm_bindgen: &syn::Path,
16911718
actual_ty: &syn::Type,
16921719
ty: &syn::Type,
16931720
shim_name: &Ident,
16941721
) -> TokenStream {
1722+
let init = static_init(wasm_bindgen, ty, shim_name);
1723+
1724+
quote! {
1725+
thread_local! {
1726+
#[automatically_derived]
1727+
#vis static #name: #actual_ty = {
1728+
#init
1729+
};
1730+
}
1731+
}
1732+
}
1733+
1734+
fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> TokenStream {
16951735
let abi_ret = quote! {
16961736
#wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
16971737
};
16981738
quote! {
1699-
#[automatically_derived]
1700-
#vis static #name: #wasm_bindgen::JsStatic<#actual_ty> = {
1701-
fn init() -> #ty {
1702-
#[link(wasm_import_module = "__wbindgen_placeholder__")]
1703-
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1704-
extern "C" {
1705-
fn #shim_name() -> #abi_ret;
1706-
}
1739+
#[link(wasm_import_module = "__wbindgen_placeholder__")]
1740+
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1741+
extern "C" {
1742+
fn #shim_name() -> #abi_ret;
1743+
}
17071744

1708-
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1709-
unsafe fn #shim_name() -> #abi_ret {
1710-
panic!("cannot access imported statics on non-wasm targets")
1711-
}
1745+
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1746+
unsafe fn #shim_name() -> #abi_ret {
1747+
panic!("cannot access imported statics on non-wasm targets")
1748+
}
17121749

1713-
unsafe {
1714-
<#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join())
1715-
}
1716-
}
1717-
thread_local!(static _VAL: #ty = init(););
1718-
#wasm_bindgen::JsStatic {
1719-
__inner: &_VAL,
1720-
}
1721-
};
1750+
unsafe {
1751+
<#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join())
1752+
}
17221753
}
17231754
}
17241755

crates/js-sys/tests/wasm/Function.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ use wasm_bindgen_test::*;
55

66
#[wasm_bindgen]
77
extern "C" {
8-
#[wasm_bindgen(js_name = max, js_namespace = Math)]
8+
#[wasm_bindgen(thread_local, js_name = max, js_namespace = Math)]
99
static MAX: Function;
1010

1111
type ArrayPrototype;
1212
#[wasm_bindgen(method, getter, structural)]
1313
pub fn push(this: &ArrayPrototype) -> Function;
14-
#[wasm_bindgen(js_name = prototype, js_namespace = Array)]
14+
#[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Array)]
1515
static ARRAY_PROTOTYPE2: ArrayPrototype;
1616
}
1717

@@ -21,12 +21,19 @@ fn apply() {
2121
args.push(&1.into());
2222
args.push(&2.into());
2323
args.push(&3.into());
24-
assert_eq!(MAX.apply(&JsValue::undefined(), &args).unwrap(), 3);
24+
assert_eq!(
25+
MAX.with(|max| max.apply(&JsValue::undefined(), &args))
26+
.unwrap(),
27+
3
28+
);
2529

2630
let arr = JsValue::from(Array::new());
2731
let args = Array::new();
2832
args.push(&1.into());
29-
ARRAY_PROTOTYPE2.push().apply(&arr, &args).unwrap();
33+
ARRAY_PROTOTYPE2
34+
.with(ArrayPrototype::push)
35+
.apply(&arr, &args)
36+
.unwrap();
3037
assert_eq!(Array::from(&arr).length(), 1);
3138
}
3239

@@ -111,24 +118,29 @@ fn bind3() {
111118

112119
#[wasm_bindgen_test]
113120
fn length() {
114-
assert_eq!(MAX.length(), 2);
115-
assert_eq!(ARRAY_PROTOTYPE2.push().length(), 1);
121+
assert_eq!(MAX.with(Function::length), 2);
122+
assert_eq!(ARRAY_PROTOTYPE2.with(ArrayPrototype::push).length(), 1);
116123
}
117124

118125
#[wasm_bindgen_test]
119126
fn name() {
120-
assert_eq!(JsValue::from(MAX.name()), "max");
121-
assert_eq!(JsValue::from(ARRAY_PROTOTYPE2.push().name()), "push");
127+
assert_eq!(JsValue::from(MAX.with(Function::name)), "max");
128+
assert_eq!(
129+
JsValue::from(ARRAY_PROTOTYPE2.with(ArrayPrototype::push).name()),
130+
"push"
131+
);
122132
}
123133

124134
#[wasm_bindgen_test]
125135
fn to_string() {
126-
assert!(MAX.to_string().length() > 0);
136+
assert!(MAX.with(Function::to_string).length() > 0);
127137
}
128138

129139
#[wasm_bindgen_test]
130140
fn function_inheritance() {
131-
assert!(MAX.is_instance_of::<Function>());
132-
assert!(MAX.is_instance_of::<Object>());
133-
let _: &Object = MAX.as_ref();
141+
assert!(MAX.with(Function::is_instance_of::<Function>));
142+
assert!(MAX.with(Function::is_instance_of::<Object>));
143+
MAX.with(|max| {
144+
let _: &Object = max.as_ref();
145+
});
134146
}

crates/js-sys/tests/wasm/Object.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ extern "C" {
99
#[wasm_bindgen(method, setter, structural)]
1010
fn set_foo(this: &Foo42, val: JsValue);
1111

12-
#[wasm_bindgen(js_name = prototype, js_namespace = Object)]
12+
#[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Object)]
1313
static OBJECT_PROTOTYPE: JsValue;
14-
#[wasm_bindgen(js_name = prototype, js_namespace = Array)]
14+
#[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Array)]
1515
static ARRAY_PROTOTYPE: JsValue;
1616

1717
type DefinePropertyAttrs;
@@ -32,9 +32,9 @@ extern "C" {
3232
#[wasm_bindgen(constructor)]
3333
fn new() -> Foo;
3434

35-
#[wasm_bindgen(js_name = prototype, js_namespace = Foo)]
35+
#[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Foo)]
3636
static FOO_PROTOTYPE: Object;
37-
#[wasm_bindgen(js_name = prototype, js_namespace = Bar)]
37+
#[wasm_bindgen(thread_local, js_name = prototype, js_namespace = Bar)]
3838
static BAR_PROTOTYPE: Object;
3939
}
4040

@@ -178,9 +178,9 @@ fn get_own_property_symbols() {
178178
#[wasm_bindgen_test]
179179
fn get_prototype_of() {
180180
let proto = JsValue::from(Object::get_prototype_of(&Object::new().into()));
181-
assert_eq!(proto, *OBJECT_PROTOTYPE);
181+
OBJECT_PROTOTYPE.with(|op| assert_eq!(proto, *op));
182182
let proto = JsValue::from(Object::get_prototype_of(&Array::new().into()));
183-
assert_eq!(proto, *ARRAY_PROTOTYPE);
183+
ARRAY_PROTOTYPE.with(|ap| assert_eq!(proto, *ap));
184184
}
185185

186186
#[wasm_bindgen_test]
@@ -249,8 +249,8 @@ fn is_sealed() {
249249
#[wasm_bindgen_test]
250250
fn is_prototype_of() {
251251
let foo = JsValue::from(Foo::new());
252-
assert!(FOO_PROTOTYPE.is_prototype_of(&foo));
253-
assert!(!BAR_PROTOTYPE.is_prototype_of(&foo));
252+
assert!(FOO_PROTOTYPE.with(|fp| fp.is_prototype_of(&foo)));
253+
assert!(!BAR_PROTOTYPE.with(|bp| bp.is_prototype_of(&foo)));
254254
}
255255

256256
#[wasm_bindgen_test]

crates/macro-support/src/parser.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ macro_rules! attrgen {
9393
(typescript_type, TypeScriptType(Span, String, Span)),
9494
(getter_with_clone, GetterWithClone(Span)),
9595
(static_string, StaticString(Span)),
96+
(thread_local, ThreadLocal(Span)),
9697

9798
// For testing purposes only.
9899
(assert_no_shim, AssertNoShim(Span)),
@@ -753,6 +754,13 @@ impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule
753754
bail_span!(self.mutability, "cannot import mutable globals yet")
754755
}
755756

757+
if let Some(span) = opts.static_string() {
758+
return Err(Diagnostic::span_error(
759+
*span,
760+
"static strings require a string literal",
761+
));
762+
}
763+
756764
let default_name = self.ident.to_string();
757765
let js_name = opts
758766
.js_name()
@@ -764,6 +772,8 @@ impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule
764772
self.ident,
765773
ShortHash((&js_name, module, &self.ident)),
766774
);
775+
let thread_local = opts.thread_local().is_some();
776+
767777
opts.check_used();
768778
Ok(ast::ImportKind::Static(ast::ImportStatic {
769779
ty: *self.ty,
@@ -772,6 +782,7 @@ impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule
772782
js_name,
773783
shim: Ident::new(&shim, Span::call_site()),
774784
wasm_bindgen: program.wasm_bindgen.clone(),
785+
thread_local,
775786
}))
776787
}
777788
}
@@ -805,7 +816,14 @@ impl<'a> ConvertToAst<(&ast::Program, BindgenAttrs, &'a Option<ast::ImportModule
805816
if opts.static_string().is_none() {
806817
bail_span!(
807818
self,
808-
"statics strings require `#[wasm_bindgen(static_string)]`"
819+
"static strings require `#[wasm_bindgen(static_string)]`"
820+
)
821+
}
822+
823+
if opts.thread_local().is_none() {
824+
bail_span!(
825+
self,
826+
"static strings require `#[wasm_bindgen(thread_local)]`"
809827
)
810828
}
811829

crates/macro/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.92" }
2626
quote = "1.0"
2727

2828
[dev-dependencies]
29+
js-sys = { path = "../js-sys" }
2930
trybuild = "1.0"
3031
wasm-bindgen = { path = "../.." }
3132
wasm-bindgen-futures = { path = "../futures" }

crates/macro/ui-tests/invalid-items.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,26 @@ pub const fn foo2() {}
1010
struct Foo<T>(T);
1111

1212
#[wasm_bindgen]
13+
#[rustfmt::skip]
1314
extern "C" {
1415
static mut FOO: u32;
1516

17+
#[wasm_bindgen(static_string)]
18+
static FOO2: JsString;
19+
20+
#[wasm_bindgen(thread_local, static_string)]
21+
static FOO3: JsString;
22+
23+
static FOO4: JsString = "test";
24+
25+
#[wasm_bindgen(static_string)]
26+
static FOO5: JsString = "test";
27+
1628
pub fn foo3(x: i32, ...);
1729
}
1830

1931
#[wasm_bindgen]
20-
extern "system" {
21-
}
32+
extern "system" {}
2233

2334
#[wasm_bindgen]
2435
pub fn foo4<T>() {}

0 commit comments

Comments
 (0)