Skip to content

Commit 0b1cce6

Browse files
authored
Add importing strings as JsString (#4055)
1 parent 33ea44f commit 0b1cce6

File tree

12 files changed

+252
-35
lines changed

12 files changed

+252
-35
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@
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";`.
76+
[#4055](https://github.com/rustwasm/wasm-bindgen/pull/4055)
77+
7578
### Changed
7679

7780
* Stabilize Web Share API.

crates/backend/src/ast.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub struct Program {
2929
pub inline_js: Vec<String>,
3030
/// Path to wasm_bindgen
3131
pub wasm_bindgen: Path,
32+
/// Path to js_sys
33+
pub js_sys: Path,
3234
/// Path to wasm_bindgen_futures
3335
pub wasm_bindgen_futures: Path,
3436
}
@@ -44,6 +46,7 @@ impl Default for Program {
4446
typescript_custom_sections: Default::default(),
4547
inline_js: Default::default(),
4648
wasm_bindgen: syn::parse_quote! { wasm_bindgen },
49+
js_sys: syn::parse_quote! { js_sys },
4750
wasm_bindgen_futures: syn::parse_quote! { wasm_bindgen_futures },
4851
}
4952
}
@@ -160,6 +163,8 @@ pub enum ImportKind {
160163
Function(ImportFunction),
161164
/// Importing a static value
162165
Static(ImportStatic),
166+
/// Importing a static string
167+
String(ImportString),
163168
/// Importing a type/class
164169
Type(ImportType),
165170
/// Importing a JS enum
@@ -272,6 +277,26 @@ pub struct ImportStatic {
272277
pub wasm_bindgen: Path,
273278
}
274279

280+
/// The type of a static string being imported
281+
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
282+
#[derive(Clone)]
283+
pub struct ImportString {
284+
/// The visibility of this static string in Rust
285+
pub vis: syn::Visibility,
286+
/// The type specified by the user, which we only use to show an error if the wrong type is used.
287+
pub ty: syn::Type,
288+
/// The name of the shim function used to access this static
289+
pub shim: Ident,
290+
/// The name of this static on the Rust side
291+
pub rust_name: Ident,
292+
/// Path to wasm_bindgen
293+
pub wasm_bindgen: Path,
294+
/// Path to js_sys
295+
pub js_sys: Path,
296+
/// The string to export.
297+
pub string: String,
298+
}
299+
275300
/// The metadata for a type being imported
276301
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
277302
#[derive(Clone)]
@@ -502,6 +527,7 @@ impl ImportKind {
502527
match *self {
503528
ImportKind::Function(_) => true,
504529
ImportKind::Static(_) => false,
530+
ImportKind::String(_) => false,
505531
ImportKind::Type(_) => false,
506532
ImportKind::Enum(_) => false,
507533
}

crates/backend/src/codegen.rs

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use quote::quote_spanned;
99
use quote::{quote, ToTokens};
1010
use std::collections::{HashMap, HashSet};
1111
use std::sync::Mutex;
12+
use syn::parse_quote;
1213
use syn::spanned::Spanned;
1314
use wasm_bindgen_shared as shared;
1415

@@ -846,6 +847,7 @@ impl TryToTokens for ast::ImportKind {
846847
match *self {
847848
ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?,
848849
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
850+
ast::ImportKind::String(ref s) => s.to_tokens(tokens),
849851
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
850852
ast::ImportKind::Enum(ref e) => e.to_tokens(tokens),
851853
}
@@ -1477,6 +1479,7 @@ impl<'a> ToTokens for DescribeImport<'a> {
14771479
let f = match *self.kind {
14781480
ast::ImportKind::Function(ref f) => f,
14791481
ast::ImportKind::Static(_) => return,
1482+
ast::ImportKind::String(_) => return,
14801483
ast::ImportKind::Type(_) => return,
14811484
ast::ImportKind::Enum(_) => return,
14821485
};
@@ -1641,44 +1644,19 @@ impl ToTokens for ast::Enum {
16411644

16421645
impl ToTokens for ast::ImportStatic {
16431646
fn to_tokens(&self, into: &mut TokenStream) {
1644-
let name = &self.rust_name;
16451647
let ty = &self.ty;
1646-
let shim_name = &self.shim;
1647-
let vis = &self.vis;
1648-
let wasm_bindgen = &self.wasm_bindgen;
1649-
1650-
let abi_ret = quote! {
1651-
#wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1652-
};
1653-
(quote! {
1654-
#[automatically_derived]
1655-
#vis static #name: #wasm_bindgen::JsStatic<#ty> = {
1656-
fn init() -> #ty {
1657-
#[link(wasm_import_module = "__wbindgen_placeholder__")]
1658-
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1659-
extern "C" {
1660-
fn #shim_name() -> #abi_ret;
1661-
}
1662-
1663-
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1664-
unsafe fn #shim_name() -> #abi_ret {
1665-
panic!("cannot access imported statics on non-wasm targets")
1666-
}
1667-
1668-
unsafe {
1669-
<#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join())
1670-
}
1671-
}
1672-
thread_local!(static _VAL: #ty = init(););
1673-
#wasm_bindgen::JsStatic {
1674-
__inner: &_VAL,
1675-
}
1676-
};
1677-
})
1648+
static_import(
1649+
&self.vis,
1650+
&self.rust_name,
1651+
&self.wasm_bindgen,
1652+
ty,
1653+
ty,
1654+
&self.shim,
1655+
)
16781656
.to_tokens(into);
16791657

16801658
Descriptor {
1681-
ident: shim_name,
1659+
ident: &self.shim,
16821660
inner: quote! {
16831661
<#ty as WasmDescribe>::describe();
16841662
},
@@ -1689,6 +1667,61 @@ impl ToTokens for ast::ImportStatic {
16891667
}
16901668
}
16911669

1670+
impl ToTokens for ast::ImportString {
1671+
fn to_tokens(&self, into: &mut TokenStream) {
1672+
let js_sys = &self.js_sys;
1673+
let actual_ty: syn::Type = parse_quote!(#js_sys::JsString);
1674+
1675+
static_import(
1676+
&self.vis,
1677+
&self.rust_name,
1678+
&self.wasm_bindgen,
1679+
&actual_ty,
1680+
&self.ty,
1681+
&self.shim,
1682+
)
1683+
.to_tokens(into);
1684+
}
1685+
}
1686+
1687+
fn static_import(
1688+
vis: &syn::Visibility,
1689+
name: &Ident,
1690+
wasm_bindgen: &syn::Path,
1691+
actual_ty: &syn::Type,
1692+
ty: &syn::Type,
1693+
shim_name: &Ident,
1694+
) -> TokenStream {
1695+
let abi_ret = quote! {
1696+
#wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1697+
};
1698+
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+
}
1707+
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+
}
1712+
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+
};
1722+
}
1723+
}
1724+
16921725
/// Emits the necessary glue tokens for "descriptor", generating an appropriate
16931726
/// symbol name as well as attributes around the descriptor function itself.
16941727
struct Descriptor<'a, T> {

crates/backend/src/encode.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ fn shared_import_kind<'a>(
297297
Ok(match i {
298298
ast::ImportKind::Function(f) => ImportKind::Function(shared_import_function(f, intern)?),
299299
ast::ImportKind::Static(f) => ImportKind::Static(shared_import_static(f, intern)),
300+
ast::ImportKind::String(f) => ImportKind::String(shared_import_string(f, intern)),
300301
ast::ImportKind::Type(f) => ImportKind::Type(shared_import_type(f, intern)),
301302
ast::ImportKind::Enum(f) => ImportKind::Enum(shared_import_enum(f, intern)),
302303
})
@@ -332,6 +333,13 @@ fn shared_import_static<'a>(i: &'a ast::ImportStatic, intern: &'a Interner) -> I
332333
}
333334
}
334335

336+
fn shared_import_string<'a>(i: &'a ast::ImportString, intern: &'a Interner) -> ImportString<'a> {
337+
ImportString {
338+
shim: intern.intern(&i.shim),
339+
string: &i.string,
340+
}
341+
}
342+
335343
fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> ImportType<'a> {
336344
ImportType {
337345
name: &i.js_name,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,19 @@ impl<'a> Context<'a> {
30673067
self.import_name(js)
30683068
}
30693069

3070+
AuxImport::String(string) => {
3071+
assert!(kind == AdapterJsImportKind::Normal);
3072+
assert!(!variadic);
3073+
assert_eq!(args.len(), 0);
3074+
3075+
let mut escaped = String::with_capacity(string.len());
3076+
string.chars().for_each(|c| match c {
3077+
'`' | '\\' | '$' => escaped.extend(['\\', c]),
3078+
_ => escaped.extend([c]),
3079+
});
3080+
Ok(format!("`{escaped}`"))
3081+
}
3082+
30703083
AuxImport::Closure {
30713084
dtor,
30723085
mutable,

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ impl<'a> Context<'a> {
567567
match &import.kind {
568568
decode::ImportKind::Function(f) => self.import_function(&import, f),
569569
decode::ImportKind::Static(s) => self.import_static(&import, s),
570+
decode::ImportKind::String(s) => self.import_string(s),
570571
decode::ImportKind::Type(t) => self.import_type(&import, t),
571572
decode::ImportKind::Enum(_) => Ok(()),
572573
}
@@ -803,6 +804,32 @@ impl<'a> Context<'a> {
803804
Ok(())
804805
}
805806

807+
fn import_string(&mut self, string: &decode::ImportString<'_>) -> Result<(), Error> {
808+
let (import_id, _id) = match self.function_imports.get(string.shim) {
809+
Some(pair) => *pair,
810+
None => return Ok(()),
811+
};
812+
813+
// Register the signature of this imported shim
814+
let id = self.import_adapter(
815+
import_id,
816+
Function {
817+
arguments: Vec::new(),
818+
shim_idx: 0,
819+
ret: Descriptor::Externref,
820+
inner_ret: None,
821+
},
822+
AdapterJsImportKind::Normal,
823+
)?;
824+
825+
// And then save off that this function is is an instanceof shim for an
826+
// imported item.
827+
self.aux
828+
.import_map
829+
.insert(id, AuxImport::String(string.string.to_owned()));
830+
Ok(())
831+
}
832+
806833
fn import_type(
807834
&mut self,
808835
import: &decode::Import<'_>,

crates/cli-support/src/wit/nonstandard.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ pub enum AuxImport {
220220
/// `JsImport`.
221221
Static(JsImport),
222222

223+
/// This import is expected to be a shim that returns an exported `JsString`.
224+
String(String),
225+
223226
/// This import is intended to manufacture a JS closure with the given
224227
/// signature and then return that back to Rust.
225228
Closure {

0 commit comments

Comments
 (0)