From c1a093cbe7761bf45abd0857a824433fa5ba5064 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Wed, 17 Aug 2022 21:28:35 +0200 Subject: [PATCH 01/26] Add support for linked modules --- crates/backend/src/ast.rs | 16 +++++ crates/backend/src/codegen.rs | 80 ++++++++++++++++------ crates/backend/src/encode.rs | 15 ++++ crates/cli-support/src/js/mod.rs | 36 ++++++++-- crates/cli-support/src/wit/mod.rs | 27 ++++++++ crates/cli-support/src/wit/nonstandard.rs | 4 ++ crates/cli-support/src/wit/section.rs | 3 + crates/macro-support/src/lib.rs | 12 ++++ crates/macro-support/src/parser.rs | 83 ++++++++++++++++------- crates/macro/src/lib.rs | 13 ++++ crates/shared/src/lib.rs | 6 ++ src/lib.rs | 2 + 12 files changed, 248 insertions(+), 49 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 7703e2570f2..8b116bd3223 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -16,6 +16,8 @@ pub struct Program { pub exports: Vec, /// js -> rust interfaces pub imports: Vec, + /// linked-to modules + pub linked_modules: Vec, /// rust enums pub enums: Vec, /// rust structs @@ -38,6 +40,12 @@ impl Program { } } +/// An abstract syntax tree representing a link to a module in Rust. +/// In contrast to Program, LinkToModule must expand to an expression. +/// linked_modules of the inner Program must contain exactly one element +/// whose link is produced by the expression. +pub struct LinkToModule(pub Program); + /// A rust to js interface. Allows interaction with rust objects/functions /// from javascript. #[cfg_attr(feature = "extra-traits", derive(Debug))] @@ -108,6 +116,14 @@ impl Hash for ImportModule { } } +impl ImportModule { + /// Name of the link function when the ImportModule is used in + /// Program::linked_modules. + pub fn link_function_name(&self) -> String { + format!("__wbindgen_link_{}", crate::util::ShortHash(self)) + } +} + /// The type of item being imported #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 5be0fad3350..00470241c33 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -130,6 +130,32 @@ impl TryToTokens for ast::Program { } } +impl TryToTokens for ast::LinkToModule { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { + let mut program = TokenStream::new(); + self.0.try_to_tokens(&mut program)?; + let link_function_name = self.0.linked_modules[0].link_function_name(); + let name = Ident::new(&link_function_name, Span::call_site()); + let abi_ret = quote! { ::Abi }; + let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret); + quote! { + { + #program + #extern_fn + + unsafe { + let _ret = #name(); + wasm_bindgen::__rt::take_last_exception().map(|()| { + ::from_abi(_ret) + }) + } + } + } + .to_tokens(tokens); + Ok(()) + } +} + impl ToTokens for ast::Struct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.rust_name; @@ -1076,8 +1102,8 @@ impl TryToTokens for ast::ImportFunction { let import_name = &self.shim; let attrs = &self.function.rust_attrs; let arguments = &arguments; - let abi_arguments = &abi_arguments; - let abi_argument_names = &abi_argument_names; + let abi_arguments = &abi_arguments[..]; + let abi_argument_names = &abi_argument_names[..]; let doc_comment = &self.doc_comment; let me = if is_method { @@ -1102,23 +1128,13 @@ impl TryToTokens for ast::ImportFunction { // like rustc itself doesn't do great in that regard so let's just do // the best we can in the meantime. let extern_fn = respan( - quote! { - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - #(#attrs)* - #[link(wasm_import_module = "__wbindgen_placeholder__")] - extern "C" { - fn #import_name(#(#abi_arguments),*) -> #abi_ret; - } - - #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] - unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret { - #( - drop(#abi_argument_names); - )* - panic!("cannot call wasm-bindgen imported functions on \ - non-wasm targets"); - } - }, + extern_fn( + import_name, + attrs, + abi_arguments, + abi_argument_names, + abi_ret, + ), &self.rust_name, ); @@ -1361,6 +1377,32 @@ impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> { } } +fn extern_fn( + import_name: &Ident, + attrs: &[syn::Attribute], + abi_arguments: &[TokenStream], + abi_argument_names: &[Ident], + abi_ret: TokenStream, +) -> TokenStream { + quote! { + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + #(#attrs)* + #[link(wasm_import_module = "__wbindgen_placeholder__")] + extern "C" { + fn #import_name(#(#abi_arguments),*) -> #abi_ret; + } + + #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] + unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret { + #( + drop(#abi_argument_names); + )* + panic!("cannot call wasm-bindgen imported functions on \ + non-wasm targets"); + } + } +} + /// Converts `span` into a stream of tokens, and attempts to ensure that `input` /// has all the appropriate span information so errors in it point to `span`. fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream { diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 41d3c8b773a..7f75b3a0b94 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -146,6 +146,11 @@ fn shared_program<'a>( .iter() .map(|x| -> &'a str { &x }) .collect(), + linked_modules: prog + .linked_modules + .iter() + .map(|a| shared_linked_module(a, intern)) + .collect::, _>>()?, local_modules: intern .files .borrow() @@ -249,6 +254,16 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( + i: &'a ast::ImportModule, + intern: &'a Interner, +) -> Result, Diagnostic> { + Ok(LinkedModule { + module: shared_module(i, intern)?, + link_function_name: intern.intern_str(&i.link_function_name()), + }) +} + fn shared_module<'a>( m: &'a ast::ImportModule, intern: &'a Interner, diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 59a10704d6b..c10f2152279 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -378,6 +378,15 @@ impl<'a> Context<'a> { // function. OutputMode::NoModules { global } => { js.push_str("const __exports = {};\n"); + js.push_str( + "\ + let script_src; + if (typeof document === 'undefined') { + script_src = location.href; + } else { + script_src = document.currentScript.src; + }\n", + ); js.push_str("let wasm;\n"); init = self.gen_init(needs_manual_start, None)?; footer.push_str(&format!( @@ -704,13 +713,7 @@ impl<'a> Context<'a> { ), OutputMode::NoModules { .. } => "\ if (typeof input === 'undefined') { - let src; - if (typeof document === 'undefined') { - src = location.href; - } else { - src = document.currentScript.src; - } - input = src.replace(/\\.js$/, '_bg.wasm'); + input = script_src.replace(/\\.js$/, '_bg.wasm'); }" .to_string(), _ => "".to_string(), @@ -3114,6 +3117,25 @@ impl<'a> Context<'a> { assert!(!variadic); self.invoke_intrinsic(intrinsic, args, prelude) } + + AuxImport::LinkTo(path) => { + assert!(kind == AdapterJsImportKind::Normal); + assert!(!variadic); + assert_eq!(args.len(), 0); + let base = match self.config.mode { + OutputMode::Web + | OutputMode::Bundler { .. } + | OutputMode::Deno + | OutputMode::Node { + experimental_modules: true, + } => "import.meta.url", + OutputMode::Node { + experimental_modules: false, + } => "__filename", + OutputMode::NoModules { .. } => "script_src", + }; + Ok(format!("new URL('{}', {}).toString()", path, base)) + } } } diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index f5b56e0079d..70c3c2a2099 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -331,6 +331,25 @@ impl<'a> Context<'a> { Ok(()) } + fn link_module(&mut self, id: ImportId, module: &decode::ImportModule) -> Result<(), Error> { + let descriptor = Function { + shim_idx: 0, + arguments: Vec::new(), + ret: Descriptor::String, + inner_ret: None, + }; + let id = self.import_adapter(id, descriptor, AdapterJsImportKind::Normal)?; + let path = match module { + decode::ImportModule::Named(n) => format!("snippets/{}", n), + decode::ImportModule::RawNamed(n) => n.to_string(), + decode::ImportModule::Inline(i) => { + format!("snippets/{}/inline{}.js", self.unique_crate_identifier, i) + } + }; + self.aux.import_map.insert(id, AuxImport::LinkTo(path)); + Ok(()) + } + fn program(&mut self, program: decode::Program<'a>) -> Result<(), Error> { self.unique_crate_identifier = program.unique_crate_identifier; let decode::Program { @@ -343,6 +362,7 @@ impl<'a> Context<'a> { inline_js, unique_crate_identifier, package_json, + linked_modules, } = program; for module in local_modules { @@ -364,6 +384,13 @@ impl<'a> Context<'a> { self.export(export)?; } + for module in linked_modules { + match self.function_imports.remove(module.link_function_name) { + Some((id, _)) => self.link_module(id, &module.module)?, + None => (), + } + } + // Register vendor prefixes for all types before we walk over all the // imports to ensure that if a vendor prefix is listed somewhere it'll // apply to all the imports. diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index 16f0cd2b39f..7f7fd369e84 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -328,6 +328,10 @@ pub enum AuxImport { /// This is an intrinsic function expected to be implemented with a JS glue /// shim. Each intrinsic has its own expected signature and implementation. Intrinsic(Intrinsic), + + /// This is a function representing a resource by returning a link to it. + /// The supplied path is relative to the JS glue shim. + LinkTo(String), } /// Values that can be imported verbatim to hook up to an import. diff --git a/crates/cli-support/src/wit/section.rs b/crates/cli-support/src/wit/section.rs index 112256193cb..b660dddf738 100644 --- a/crates/cli-support/src/wit/section.rs +++ b/crates/cli-support/src/wit/section.rs @@ -355,6 +355,9 @@ fn check_standard_import(import: &AuxImport) -> Result<(), Error> { AuxImport::Intrinsic(intrinsic) => { format!("wasm-bindgen specific intrinsic `{}`", intrinsic.name()) } + AuxImport::LinkTo(path) => { + format!("wasm-bindgen specific link function for `{}`", path) + } AuxImport::Closure { .. } => format!("creating a `Closure` wrapper"), }; bail!("import of {} requires JS glue", item); diff --git a/crates/macro-support/src/lib.rs b/crates/macro-support/src/lib.rs index b7a35ae1bed..bf9f71a9d20 100644 --- a/crates/macro-support/src/lib.rs +++ b/crates/macro-support/src/lib.rs @@ -40,6 +40,18 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> Result Result { + parser::reset_attrs_used(); + let opts = syn::parse2(input)?; + + let mut tokens = proc_macro2::TokenStream::new(); + let link = parser::link_to(opts)?; + link.try_to_tokens(&mut tokens)?; + + Ok(tokens) +} + /// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings pub fn expand_class_marker( attr: TokenStream, diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index d05f139f62e..dd212429719 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -95,6 +95,23 @@ macro_rules! methods { ($(($name:ident, $variant:ident($($contents:tt)*)),)*) => { $(methods!(@method $name, $variant($($contents)*));)* + fn enforce_used(self) -> Result<(), Diagnostic> { + // Account for the fact this method was called + ATTRS.with(|state| state.checks.set(state.checks.get() + 1)); + + let mut errors = Vec::new(); + for (used, attr) in self.attrs.iter() { + if used.get() { + continue + } + let span = match attr { + $(BindgenAttr::$variant(span, ..) => span,)* + }; + errors.push(Diagnostic::span_error(*span, "unused #[wasm_bindgen] attribute")); + } + Diagnostic::from_vec(errors) + } + fn check_used(self) { // Account for the fact this method was called ATTRS.with(|state| { @@ -1366,29 +1383,7 @@ impl MacroParse for syn::ItemForeignMod { )); } } - let module = if let Some((name, span)) = opts.module() { - if opts.inline_js().is_some() { - let msg = "cannot specify both `module` and `inline_js`"; - errors.push(Diagnostic::span_error(span, msg)); - } - if opts.raw_module().is_some() { - let msg = "cannot specify both `module` and `raw_module`"; - errors.push(Diagnostic::span_error(span, msg)); - } - Some(ast::ImportModule::Named(name.to_string(), span)) - } else if let Some((name, span)) = opts.raw_module() { - if opts.inline_js().is_some() { - let msg = "cannot specify both `raw_module` and `inline_js`"; - errors.push(Diagnostic::span_error(span, msg)); - } - Some(ast::ImportModule::RawNamed(name.to_string(), span)) - } else if let Some((js, span)) = opts.inline_js() { - let i = program.inline_js.len(); - program.inline_js.push(js.to_string()); - Some(ast::ImportModule::Inline(i, span)) - } else { - None - }; + let module = module_from_opts(program, &opts, &mut errors); for item in self.items.into_iter() { if let Err(e) = item.macro_parse(program, module.clone()) { errors.push(e); @@ -1433,6 +1428,36 @@ impl MacroParse> for syn::ForeignItem { } } +pub fn module_from_opts( + program: &mut ast::Program, + opts: &BindgenAttrs, + errors: &mut Vec, +) -> Option { + if let Some((name, span)) = opts.module() { + if opts.inline_js().is_some() { + let msg = "cannot specify both `module` and `inline_js`"; + errors.push(Diagnostic::span_error(span, msg)); + } + if opts.raw_module().is_some() { + let msg = "cannot specify both `module` and `raw_module`"; + errors.push(Diagnostic::span_error(span, msg)); + } + Some(ast::ImportModule::Named(name.to_string(), span)) + } else if let Some((name, span)) = opts.raw_module() { + if opts.inline_js().is_some() { + let msg = "cannot specify both `raw_module` and `inline_js`"; + errors.push(Diagnostic::span_error(span, msg)); + } + Some(ast::ImportModule::RawNamed(name.to_string(), span)) + } else if let Some((js, span)) = opts.inline_js() { + let i = program.inline_js.len(); + program.inline_js.push(js.to_string()); + Some(ast::ImportModule::Inline(i, span)) + } else { + None + } +} + /// Get the first type parameter of a generic type, errors on incorrect input. fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result, Diagnostic> { let t = match ty { @@ -1649,3 +1674,15 @@ fn operation_kind(opts: &BindgenAttrs) -> ast::OperationKind { } operation_kind } + +pub fn link_to(opts: BindgenAttrs) -> Result { + let mut program = ast::Program::default(); + let mut errors = Vec::new(); + let module = module_from_opts(&mut program, &opts, &mut errors).ok_or( + Diagnostic::span_error(Span::call_site(), "`link_to!` requires a module."), + )?; + opts.enforce_used()?; + program.linked_modules.push(module); + Diagnostic::from_vec(errors)?; + Ok(ast::LinkToModule(program)) +} diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index c677aaf24bf..3f3bb279f09 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -18,6 +18,19 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { } } +#[proc_macro] +pub fn link_to(input: TokenStream) -> TokenStream { + match wasm_bindgen_macro_support::expand_link_to(input.into()) { + Ok(tokens) => { + if cfg!(feature = "xxx_debug_only_print_generated_code") { + println!("{}", tokens); + } + tokens.into() + } + Err(diagnostic) => (quote! { Result::::Ok(#diagnostic) }).into(), + } +} + #[proc_macro_attribute] pub fn __wasm_bindgen_class_marker(attr: TokenStream, input: TokenStream) -> TokenStream { match wasm_bindgen_macro_support::expand_class_marker(attr.into(), input.into()) { diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index f69d8f4a855..210c63a794a 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -22,6 +22,7 @@ macro_rules! shared_api { inline_js: Vec<&'a str>, unique_crate_identifier: &'a str, package_json: Option<&'a str>, + linked_modules: Vec>, } struct Import<'a> { @@ -30,6 +31,11 @@ macro_rules! shared_api { kind: ImportKind<'a>, } + struct LinkedModule<'a> { + module: ImportModule<'a>, + link_function_name: &'a str, + } + enum ImportModule<'a> { Named(&'a str), RawNamed(&'a str), diff --git a/src/lib.rs b/src/lib.rs index 2ccb284b9a7..559d8ea88c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,6 +63,8 @@ pub mod prelude { pub use crate::JsError; } +pub use wasm_bindgen_macro::link_to; + pub mod convert; pub mod describe; From 5b8f2f59fa11d9f957b8c1f99f11c1d1dd2ba10a Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Thu, 18 Aug 2022 17:27:23 +0200 Subject: [PATCH 02/26] Use `wasm_bindgen::link_to!` in wasm-bindgen-futures --- crates/futures/src/task/wait_async_polyfill.rs | 18 +++--------------- crates/futures/src/task/worker.js | 6 ++++++ 2 files changed, 9 insertions(+), 15 deletions(-) create mode 100644 crates/futures/src/task/worker.js diff --git a/crates/futures/src/task/wait_async_polyfill.rs b/crates/futures/src/task/wait_async_polyfill.rs index 14b8f0eadf5..6d4f8077d48 100644 --- a/crates/futures/src/task/wait_async_polyfill.rs +++ b/crates/futures/src/task/wait_async_polyfill.rs @@ -36,22 +36,13 @@ * when possible. The worker communicates with its parent using postMessage. */ -use js_sys::{encode_uri_component, Array, Promise}; +use js_sys::{Array, Promise}; use std::cell::RefCell; use std::sync::atomic::AtomicI32; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use web_sys::{MessageEvent, Worker}; -const HELPER_CODE: &'static str = " -onmessage = function (ev) { - let [ia, index, value] = ev.data; - ia = new Int32Array(ia.buffer); - let result = Atomics.wait(ia, index, value); - postMessage(result); -}; -"; - thread_local! { static HELPERS: RefCell> = RefCell::new(vec![]); } @@ -62,11 +53,8 @@ fn alloc_helper() -> Worker { return helper; } - let mut initialization_string = "data:application/javascript,".to_owned(); - let encoded: String = encode_uri_component(HELPER_CODE).into(); - initialization_string.push_str(&encoded); - - Worker::new(&initialization_string).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) + let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js").unwrap(); + Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) }) } diff --git a/crates/futures/src/task/worker.js b/crates/futures/src/task/worker.js new file mode 100644 index 00000000000..d25dab6606d --- /dev/null +++ b/crates/futures/src/task/worker.js @@ -0,0 +1,6 @@ +onmessage = function (ev) { + let [ia, index, value] = ev.data; + ia = new Int32Array(ia.buffer); + let result = Atomics.wait(ia, index, value); + postMessage(result); +}; From 3f631c759464d054ed1ed817888743148a7fc103 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 6 Sep 2022 01:15:45 +0200 Subject: [PATCH 03/26] Fix tests --- crates/backend/src/codegen.rs | 4 ++-- crates/cli/tests/wasm-bindgen/main.rs | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 00470241c33..9dfacabd54c 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -138,7 +138,7 @@ impl TryToTokens for ast::LinkToModule { let name = Ident::new(&link_function_name, Span::call_site()); let abi_ret = quote! { ::Abi }; let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret); - quote! { + (quote! { { #program #extern_fn @@ -150,7 +150,7 @@ impl TryToTokens for ast::LinkToModule { }) } } - } + }) .to_tokens(tokens); Ok(()) } diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index b52f14e64c3..1fb94f7c97b 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -258,17 +258,19 @@ fn default_module_path_target_no_modules() { cmd.assert().success(); let contents = fs::read_to_string(out_dir.join("default_module_path_target_no_modules.js")).unwrap(); + assert!(contents.contains( + "\ + if (typeof document === 'undefined') { + script_src = location.href; + } else { + script_src = document.currentScript.src; + }", + )); assert!(contents.contains( "\ async function init(input) { if (typeof input === 'undefined') { - let src; - if (typeof document === 'undefined') { - src = location.href; - } else { - src = document.currentScript.src; - } - input = src.replace(/\\.js$/, '_bg.wasm'); + input = script_src.replace(/\\.js$/, '_bg.wasm'); }", )); } From fc615e64ec2b25502123b92ede874b893d635689 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 6 Sep 2022 01:29:50 +0200 Subject: [PATCH 04/26] Update schema --- crates/shared/src/lib.rs | 2 +- crates/shared/src/schema_hash_approval.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 210c63a794a..408613be9f1 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -6,7 +6,7 @@ mod schema_hash_approval; // This gets changed whenever our schema changes. // At this time versions of wasm-bindgen and wasm-bindgen-cli are required to have the exact same // SCHEMA_VERSION in order to work together. -pub const SCHEMA_VERSION: &str = "0.2.83"; +pub const SCHEMA_VERSION: &str = "0.2.84"; #[macro_export] macro_rules! shared_api { diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 4692a28c81e..0d4d0bb38f1 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &'static str = "17656911631008664055"; +const APPROVED_SCHEMA_FILE_HASH: &'static str = "584864585234329974"; #[test] fn schema_version() { From 74bec76ca204ac59954e4173709eda7f67382fd8 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Sat, 24 Sep 2022 20:44:59 +0200 Subject: [PATCH 05/26] Return `String` instead of `Result` --- crates/backend/src/codegen.rs | 5 +---- crates/futures/src/task/wait_async_polyfill.rs | 2 +- crates/macro/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 9dfacabd54c..963ef1734de 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -144,10 +144,7 @@ impl TryToTokens for ast::LinkToModule { #extern_fn unsafe { - let _ret = #name(); - wasm_bindgen::__rt::take_last_exception().map(|()| { - ::from_abi(_ret) - }) + ::from_abi(#name()) } } }) diff --git a/crates/futures/src/task/wait_async_polyfill.rs b/crates/futures/src/task/wait_async_polyfill.rs index 6d4f8077d48..3e5efe813cb 100644 --- a/crates/futures/src/task/wait_async_polyfill.rs +++ b/crates/futures/src/task/wait_async_polyfill.rs @@ -53,7 +53,7 @@ fn alloc_helper() -> Worker { return helper; } - let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js").unwrap(); + let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js"); Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) }) } diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index 3f3bb279f09..7dbaac5357b 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -27,7 +27,7 @@ pub fn link_to(input: TokenStream) -> TokenStream { } tokens.into() } - Err(diagnostic) => (quote! { Result::::Ok(#diagnostic) }).into(), + Err(diagnostic) => (quote! { String::clone(#diagnostic) }).into(), } } From 18bf892c3e0749410bf9d3a1ac5e672cf7b70af9 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Sat, 24 Sep 2022 21:01:56 +0200 Subject: [PATCH 06/26] Add documentation --- crates/macro/Cargo.toml | 1 + crates/macro/src/lib.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index f7a0bbb2176..5c0c85a63a7 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [lib] proc-macro = true +doctest = false # 'cannot call wasm-bindgen imported functions on non-wasm targets' [features] spans = ["wasm-bindgen-macro-support/spans"] diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index 7dbaac5357b..0bd6944a2c1 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -18,6 +18,13 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { } } +/// This macro adds a linked module by module, raw_module or inline_js attribute. +/// It expands to a String containing a link to that module. This link may be relative and +/// is suitable for dynamic imports, or creating workers or worklets: +/// ``` +/// use web_sys::Worker; +/// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js")); +/// ``` #[proc_macro] pub fn link_to(input: TokenStream) -> TokenStream { match wasm_bindgen_macro_support::expand_link_to(input.into()) { From b8b13630237ce698cd5bb212c14b548a499a92cb Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Sat, 24 Sep 2022 21:38:59 +0200 Subject: [PATCH 07/26] Add tests --- crates/macro/ui-tests/link-to.rs | 24 ++++++++++++++++++++++++ crates/macro/ui-tests/link-to.stderr | 19 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 crates/macro/ui-tests/link-to.rs create mode 100644 crates/macro/ui-tests/link-to.stderr diff --git a/crates/macro/ui-tests/link-to.rs b/crates/macro/ui-tests/link-to.rs new file mode 100644 index 00000000000..10ddd34bfd1 --- /dev/null +++ b/crates/macro/ui-tests/link-to.rs @@ -0,0 +1,24 @@ +fn good1() -> String { + wasm_bindgen::link_to!(inline_js = "console.log('Hello world!');") +} + +fn good2() -> String { + wasm_bindgen::link_to!(raw_module = "package/foo.js") +} + +fn bad1() -> String { + wasm_bindgen::link_to!(module = "/src/not-found.js") +} + +fn bad2() -> String { + wasm_bindgen::link_to!() +} + +fn bad3() -> String { + wasm_bindgen::link_to!( + inline_js = "console.log('Hello world!');", + js_namespace = foo + ) +} + +fn main() {} diff --git a/crates/macro/ui-tests/link-to.stderr b/crates/macro/ui-tests/link-to.stderr new file mode 100644 index 00000000000..fc3c91f555a --- /dev/null +++ b/crates/macro/ui-tests/link-to.stderr @@ -0,0 +1,19 @@ +error: failed to read file `$WORKSPACE/target/tests/wasm-bindgen-macro/src/not-found.js`: No such file or directory (os error 2) + --> $DIR/link-to.rs:10:37 + | +10 | wasm_bindgen::link_to!(module = "/src/not-found.js") + | ^^^^^^^^^^^^^^^^^^^ + +error: `link_to!` requires a module. + --> $DIR/link-to.rs:14:5 + | +14 | wasm_bindgen::link_to!() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `wasm_bindgen::link_to` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unused #[wasm_bindgen] attribute + --> $DIR/link-to.rs:20:9 + | +20 | js_namespace = foo + | ^^^^^^^^^^^^ From c64b65e81f606a5b457c6e87d3b08e535c483771 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Wed, 7 Sep 2022 17:38:04 +0200 Subject: [PATCH 08/26] Refactor: Return Diagnostic from module_from_opts --- crates/macro-support/src/parser.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index dd212429719..918afdb2f49 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1383,7 +1383,9 @@ impl MacroParse for syn::ItemForeignMod { )); } } - let module = module_from_opts(program, &opts, &mut errors); + let module = module_from_opts(program, &opts) + .map_err(|e| errors.push(e)) + .unwrap_or_default(); for item in self.items.into_iter() { if let Err(e) = item.macro_parse(program, module.clone()) { errors.push(e); @@ -1431,9 +1433,9 @@ impl MacroParse> for syn::ForeignItem { pub fn module_from_opts( program: &mut ast::Program, opts: &BindgenAttrs, - errors: &mut Vec, -) -> Option { - if let Some((name, span)) = opts.module() { +) -> Result, Diagnostic> { + let mut errors = Vec::new(); + let module = if let Some((name, span)) = opts.module() { if opts.inline_js().is_some() { let msg = "cannot specify both `module` and `inline_js`"; errors.push(Diagnostic::span_error(span, msg)); @@ -1455,7 +1457,9 @@ pub fn module_from_opts( Some(ast::ImportModule::Inline(i, span)) } else { None - } + }; + Diagnostic::from_vec(errors)?; + Ok(module) } /// Get the first type parameter of a generic type, errors on incorrect input. @@ -1677,12 +1681,10 @@ fn operation_kind(opts: &BindgenAttrs) -> ast::OperationKind { pub fn link_to(opts: BindgenAttrs) -> Result { let mut program = ast::Program::default(); - let mut errors = Vec::new(); - let module = module_from_opts(&mut program, &opts, &mut errors).ok_or( - Diagnostic::span_error(Span::call_site(), "`link_to!` requires a module."), - )?; + let module = module_from_opts(&mut program, &opts)?.ok_or_else(|| { + Diagnostic::span_error(Span::call_site(), "`link_to!` requires a module.") + })?; opts.enforce_used()?; program.linked_modules.push(module); - Diagnostic::from_vec(errors)?; Ok(ast::LinkToModule(program)) } From 262fc1dc45e74abe2b3f164ff8ce6ab0758aa8d2 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Wed, 7 Sep 2022 18:04:08 +0200 Subject: [PATCH 09/26] Refactor: Use Option::filter --- crates/macro-support/src/parser.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 918afdb2f49..fb816668f10 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1373,15 +1373,11 @@ impl MacroParse for syn::ItemConst { impl MacroParse for syn::ItemForeignMod { fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> { let mut errors = Vec::new(); - match self.abi.name { - Some(ref l) if l.value() == "C" => {} - None => {} - Some(ref other) => { - errors.push(err_span!( - other, - "only foreign mods with the `C` ABI are allowed" - )); - } + if let Some(other) = self.abi.name.filter(|l| l.value() != "C") { + errors.push(err_span!( + other, + "only foreign mods with the `C` ABI are allowed" + )); } let module = module_from_opts(program, &opts) .map_err(|e| errors.push(e)) From b26ef80792f3bc64ac57354dbdb19e53234f7b6c Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 26 Sep 2022 13:33:28 +0200 Subject: [PATCH 10/26] Fix inline_js offsets --- crates/backend/src/ast.rs | 19 ++++++++++--------- crates/backend/src/codegen.rs | 2 +- crates/backend/src/encode.rs | 6 ++++-- crates/cli-support/src/wit/mod.rs | 23 +++++++++++++++++++---- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 8b116bd3223..064e0137713 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -2,7 +2,7 @@ //! with all the added metadata necessary to generate WASM bindings //! for it. -use crate::Diagnostic; +use crate::{util::ShortHash, Diagnostic}; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; use wasm_bindgen_shared as shared; @@ -38,6 +38,15 @@ impl Program { && self.typescript_custom_sections.is_empty() && self.inline_js.is_empty() } + + /// Name of the link function for a specific linked module + pub fn link_function_name(&self, idx: usize) -> String { + let hash = match &self.linked_modules[idx] { + ImportModule::Inline(idx, _) => ShortHash((1, &self.inline_js[*idx])).to_string(), + other => ShortHash((0, other)).to_string(), + }; + format!("__wbindgen_link_{}", hash) + } } /// An abstract syntax tree representing a link to a module in Rust. @@ -116,14 +125,6 @@ impl Hash for ImportModule { } } -impl ImportModule { - /// Name of the link function when the ImportModule is used in - /// Program::linked_modules. - pub fn link_function_name(&self) -> String { - format!("__wbindgen_link_{}", crate::util::ShortHash(self)) - } -} - /// The type of item being imported #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 963ef1734de..a80ff0249ad 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -134,7 +134,7 @@ impl TryToTokens for ast::LinkToModule { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut program = TokenStream::new(); self.0.try_to_tokens(&mut program)?; - let link_function_name = self.0.linked_modules[0].link_function_name(); + let link_function_name = self.0.link_function_name(0); let name = Ident::new(&link_function_name, Span::call_site()); let abi_ret = quote! { ::Abi }; let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret); diff --git a/crates/backend/src/encode.rs b/crates/backend/src/encode.rs index 7f75b3a0b94..254d0e88537 100644 --- a/crates/backend/src/encode.rs +++ b/crates/backend/src/encode.rs @@ -149,7 +149,8 @@ fn shared_program<'a>( linked_modules: prog .linked_modules .iter() - .map(|a| shared_linked_module(a, intern)) + .enumerate() + .map(|(i, a)| shared_linked_module(&prog.link_function_name(i), a, intern)) .collect::, _>>()?, local_modules: intern .files @@ -255,12 +256,13 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result( + name: &str, i: &'a ast::ImportModule, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(LinkedModule { module: shared_module(i, intern)?, - link_function_name: intern.intern_str(&i.link_function_name()), + link_function_name: intern.intern_str(name), }) } diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 70c3c2a2099..88c6a898bd1 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -331,7 +331,12 @@ impl<'a> Context<'a> { Ok(()) } - fn link_module(&mut self, id: ImportId, module: &decode::ImportModule) -> Result<(), Error> { + fn link_module( + &mut self, + id: ImportId, + module: &decode::ImportModule, + offset: usize, + ) -> Result<(), Error> { let descriptor = Function { shim_idx: 0, arguments: Vec::new(), @@ -342,8 +347,12 @@ impl<'a> Context<'a> { let path = match module { decode::ImportModule::Named(n) => format!("snippets/{}", n), decode::ImportModule::RawNamed(n) => n.to_string(), - decode::ImportModule::Inline(i) => { - format!("snippets/{}/inline{}.js", self.unique_crate_identifier, i) + decode::ImportModule::Inline(idx) => { + format!( + "snippets/{}/inline{}.js", + self.unique_crate_identifier, + *idx as usize + offset + ) } }; self.aux.import_map.insert(id, AuxImport::LinkTo(path)); @@ -384,9 +393,15 @@ impl<'a> Context<'a> { self.export(export)?; } + let offset = self + .aux + .snippets + .get(unique_crate_identifier) + .map(|s| s.len()) + .unwrap_or(0); for module in linked_modules { match self.function_imports.remove(module.link_function_name) { - Some((id, _)) => self.link_module(id, &module.module)?, + Some((id, _)) => self.link_module(id, &module.module, offset)?, None => (), } } From 66b5b91b9bc464676964d014cd36020c6ef72f0e Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 26 Sep 2022 15:30:57 +0200 Subject: [PATCH 11/26] Fully-qualified names in quote --- crates/backend/src/codegen.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a80ff0249ad..50d9094157a 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -136,7 +136,7 @@ impl TryToTokens for ast::LinkToModule { self.0.try_to_tokens(&mut program)?; let link_function_name = self.0.link_function_name(0); let name = Ident::new(&link_function_name, Span::call_site()); - let abi_ret = quote! { ::Abi }; + let abi_ret = quote! { ::Abi }; let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret); (quote! { { @@ -144,7 +144,7 @@ impl TryToTokens for ast::LinkToModule { #extern_fn unsafe { - ::from_abi(#name()) + ::from_abi(#name()) } } }) From 7697dea8160c47863f45793843f4a1f9176ea127 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 26 Sep 2022 13:33:58 +0200 Subject: [PATCH 12/26] Return absolute URLs and add node tests --- crates/cli-support/src/js/mod.rs | 4 ++-- crates/cli/tests/wasm-bindgen/main.rs | 2 +- crates/macro/src/lib.rs | 4 ++-- tests/wasm/link_to.js | 4 ++++ tests/wasm/link_to.rs | 30 +++++++++++++++++++++++++++ tests/wasm/linked_module.js | 1 + tests/wasm/main.rs | 1 + 7 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 tests/wasm/link_to.js create mode 100644 tests/wasm/link_to.rs create mode 100644 tests/wasm/linked_module.js diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index c10f2152279..e25f1c316df 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -384,7 +384,7 @@ impl<'a> Context<'a> { if (typeof document === 'undefined') { script_src = location.href; } else { - script_src = document.currentScript.src; + script_src = new URL(document.currentScript.src, location.href).toString(); }\n", ); js.push_str("let wasm;\n"); @@ -3131,7 +3131,7 @@ impl<'a> Context<'a> { } => "import.meta.url", OutputMode::Node { experimental_modules: false, - } => "__filename", + } => "require('url').pathToFileURL(__filename)", OutputMode::NoModules { .. } => "script_src", }; Ok(format!("new URL('{}', {}).toString()", path, base)) diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index 1fb94f7c97b..e854de23e51 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -263,7 +263,7 @@ fn default_module_path_target_no_modules() { if (typeof document === 'undefined') { script_src = location.href; } else { - script_src = document.currentScript.src; + script_src = new URL(document.currentScript.src, location.href).toString(); }", )); assert!(contents.contains( diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index 0bd6944a2c1..dfd36a154c6 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -19,8 +19,8 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { } /// This macro adds a linked module by module, raw_module or inline_js attribute. -/// It expands to a String containing a link to that module. This link may be relative and -/// is suitable for dynamic imports, or creating workers or worklets: +/// It expands to a String containing a URL to that module. This URL can be used +/// to create workers or worklets, for example: /// ``` /// use web_sys::Worker; /// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js")); diff --git a/tests/wasm/link_to.js b/tests/wasm/link_to.js new file mode 100644 index 00000000000..3028a6ae8a6 --- /dev/null +++ b/tests/wasm/link_to.js @@ -0,0 +1,4 @@ +const fs = require('fs'); +const url = require('url'); + +exports.read_file = (str) => fs.readFileSync(url.fileURLToPath(str), "utf8"); diff --git a/tests/wasm/link_to.rs b/tests/wasm/link_to.rs new file mode 100644 index 00000000000..bfa5c0e6e34 --- /dev/null +++ b/tests/wasm/link_to.rs @@ -0,0 +1,30 @@ +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; + +#[wasm_bindgen(module = "/tests/wasm/link_to.js")] +extern "C" { + #[wasm_bindgen(catch)] + fn read_file(url: &str) -> Result; +} + +#[wasm_bindgen_test] +fn test_module() { + let link = wasm_bindgen::link_to!(module = "/tests/wasm/linked_module.js"); + assert_eq!(read_file(&link).unwrap(), "// linked module\n"); +} + +#[wasm_bindgen_test] +fn test_raw_module() { + let link = wasm_bindgen::link_to!(raw_module = "not-found.js"); + assert!(read_file(&link).is_err()); +} + +#[wasm_bindgen_test] +fn test_inline_js() { + // Test two invocations to ensure that snippet indices from different + // Program structs are offset correctly. + let link1 = wasm_bindgen::link_to!(inline_js = "// inline js 1\n"); + let link2 = wasm_bindgen::link_to!(inline_js = "// inline js 2\n"); + assert_eq!(read_file(&link1).unwrap(), "// inline js 1\n"); + assert_eq!(read_file(&link2).unwrap(), "// inline js 2\n"); +} diff --git a/tests/wasm/linked_module.js b/tests/wasm/linked_module.js new file mode 100644 index 00000000000..b979535b28e --- /dev/null +++ b/tests/wasm/linked_module.js @@ -0,0 +1 @@ +// linked module diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 51e6dcafed5..59952861957 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -32,6 +32,7 @@ pub mod intrinsics; pub mod js_keywords; pub mod js_objects; pub mod jscast; +pub mod link_to; pub mod math; pub mod no_shims; pub mod node; From b5b443fade8a035a04e98a31d002ad96ffd7a466 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 26 Sep 2022 16:51:48 +0200 Subject: [PATCH 13/26] Fix message --- crates/macro-support/src/parser.rs | 2 +- crates/macro/ui-tests/link-to.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index fb816668f10..4c4a89c12d9 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -107,7 +107,7 @@ macro_rules! methods { let span = match attr { $(BindgenAttr::$variant(span, ..) => span,)* }; - errors.push(Diagnostic::span_error(*span, "unused #[wasm_bindgen] attribute")); + errors.push(Diagnostic::span_error(*span, "unused wasm_bindgen attribute")); } Diagnostic::from_vec(errors) } diff --git a/crates/macro/ui-tests/link-to.stderr b/crates/macro/ui-tests/link-to.stderr index fc3c91f555a..3cd32f89c98 100644 --- a/crates/macro/ui-tests/link-to.stderr +++ b/crates/macro/ui-tests/link-to.stderr @@ -12,7 +12,7 @@ error: `link_to!` requires a module. | = note: this error originates in the macro `wasm_bindgen::link_to` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unused #[wasm_bindgen] attribute +error: unused wasm_bindgen attribute --> $DIR/link-to.rs:20:9 | 20 | js_namespace = foo From 562d6387a8a0f9b59da5b740c01c67ae1afe60b7 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 27 Sep 2022 18:14:47 +0200 Subject: [PATCH 14/26] Enable compile doctest for example --- crates/macro/Cargo.toml | 2 +- crates/macro/src/lib.rs | 2 +- crates/macro/src/worker.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 crates/macro/src/worker.js diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index 5c0c85a63a7..9807e6870c4 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" [lib] proc-macro = true -doctest = false # 'cannot call wasm-bindgen imported functions on non-wasm targets' [features] spans = ["wasm-bindgen-macro-support/spans"] @@ -28,3 +27,4 @@ quote = "1.0" trybuild = "1.0" wasm-bindgen = { path = "../..", version = "0.2.83" } wasm-bindgen-futures = { path = "../futures", version = "0.4.33" } +web-sys = { path = "../web-sys", version = "0.3.60" } diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index dfd36a154c6..62eff0d731d 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -21,7 +21,7 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { /// This macro adds a linked module by module, raw_module or inline_js attribute. /// It expands to a String containing a URL to that module. This URL can be used /// to create workers or worklets, for example: -/// ``` +/// ```no_run /// use web_sys::Worker; /// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js")); /// ``` diff --git a/crates/macro/src/worker.js b/crates/macro/src/worker.js new file mode 100644 index 00000000000..360d4d42923 --- /dev/null +++ b/crates/macro/src/worker.js @@ -0,0 +1 @@ +// This file is needed for the doctest of `crate::link_to`. From 4f4843b8a2b5035e4ce0d6dbc2d0c5fe44ac9c1e Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 27 Sep 2022 23:06:19 +0200 Subject: [PATCH 15/26] Disallow module paths in `link_to!` --- crates/macro-support/src/parser.rs | 8 ++++++++ crates/macro/ui-tests/link-to.rs | 14 +++++++++++--- crates/macro/ui-tests/link-to.stderr | 24 ++++++++++++++++++------ tests/wasm/link_to.rs | 2 +- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 4c4a89c12d9..698493746b3 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -1680,6 +1680,14 @@ pub fn link_to(opts: BindgenAttrs) -> Result { let module = module_from_opts(&mut program, &opts)?.ok_or_else(|| { Diagnostic::span_error(Span::call_site(), "`link_to!` requires a module.") })?; + if let ast::ImportModule::Named(p, s) | ast::ImportModule::RawNamed(p, s) = &module { + if !p.starts_with("./") && !p.starts_with("../") && !p.starts_with("/") { + return Err(Diagnostic::span_error( + *s, + "`link_to!` does not support module paths.", + )); + } + } opts.enforce_used()?; program.linked_modules.push(module); Ok(ast::LinkToModule(program)) diff --git a/crates/macro/ui-tests/link-to.rs b/crates/macro/ui-tests/link-to.rs index 10ddd34bfd1..ce51c128e2e 100644 --- a/crates/macro/ui-tests/link-to.rs +++ b/crates/macro/ui-tests/link-to.rs @@ -3,18 +3,26 @@ fn good1() -> String { } fn good2() -> String { - wasm_bindgen::link_to!(raw_module = "package/foo.js") + wasm_bindgen::link_to!(raw_module = "./foo.js") } fn bad1() -> String { - wasm_bindgen::link_to!(module = "/src/not-found.js") + wasm_bindgen::link_to!(module = "package/foo.js") } fn bad2() -> String { - wasm_bindgen::link_to!() + wasm_bindgen::link_to!(raw_module = "package/foo.js") } fn bad3() -> String { + wasm_bindgen::link_to!(module = "/src/not-found.js") +} + +fn bad4() -> String { + wasm_bindgen::link_to!() +} + +fn bad5() -> String { wasm_bindgen::link_to!( inline_js = "console.log('Hello world!');", js_namespace = foo diff --git a/crates/macro/ui-tests/link-to.stderr b/crates/macro/ui-tests/link-to.stderr index 3cd32f89c98..00565390549 100644 --- a/crates/macro/ui-tests/link-to.stderr +++ b/crates/macro/ui-tests/link-to.stderr @@ -1,19 +1,31 @@ -error: failed to read file `$WORKSPACE/target/tests/wasm-bindgen-macro/src/not-found.js`: No such file or directory (os error 2) +error: `link_to!` does not support module paths. --> $DIR/link-to.rs:10:37 | -10 | wasm_bindgen::link_to!(module = "/src/not-found.js") +10 | wasm_bindgen::link_to!(module = "package/foo.js") + | ^^^^^^^^^^^^^^^^ + +error: `link_to!` does not support module paths. + --> $DIR/link-to.rs:14:41 + | +14 | wasm_bindgen::link_to!(raw_module = "package/foo.js") + | ^^^^^^^^^^^^^^^^ + +error: failed to read file `$WORKSPACE/target/tests/wasm-bindgen-macro/src/not-found.js`: No such file or directory (os error 2) + --> $DIR/link-to.rs:18:37 + | +18 | wasm_bindgen::link_to!(module = "/src/not-found.js") | ^^^^^^^^^^^^^^^^^^^ error: `link_to!` requires a module. - --> $DIR/link-to.rs:14:5 + --> $DIR/link-to.rs:22:5 | -14 | wasm_bindgen::link_to!() +22 | wasm_bindgen::link_to!() | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `wasm_bindgen::link_to` (in Nightly builds, run with -Z macro-backtrace for more info) error: unused wasm_bindgen attribute - --> $DIR/link-to.rs:20:9 + --> $DIR/link-to.rs:28:9 | -20 | js_namespace = foo +28 | js_namespace = foo | ^^^^^^^^^^^^ diff --git a/tests/wasm/link_to.rs b/tests/wasm/link_to.rs index bfa5c0e6e34..e631bdb03d9 100644 --- a/tests/wasm/link_to.rs +++ b/tests/wasm/link_to.rs @@ -15,7 +15,7 @@ fn test_module() { #[wasm_bindgen_test] fn test_raw_module() { - let link = wasm_bindgen::link_to!(raw_module = "not-found.js"); + let link = wasm_bindgen::link_to!(raw_module = "./not-found.js"); assert!(read_file(&link).is_err()); } From 250081d655310ec31548a2a954964983bedcc900 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Fri, 14 Oct 2022 17:33:44 +0200 Subject: [PATCH 16/26] Fix documentation --- crates/cli-support/src/wit/nonstandard.rs | 4 ++-- crates/macro/src/lib.rs | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index 7f7fd369e84..5b997175a2a 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -329,8 +329,8 @@ pub enum AuxImport { /// shim. Each intrinsic has its own expected signature and implementation. Intrinsic(Intrinsic), - /// This is a function representing a resource by returning a link to it. - /// The supplied path is relative to the JS glue shim. + /// This is a function which returns a URL pointing to a specific file, + /// usually a JS snippet. The supplied path is relative to the JS glue shim. LinkTo(String), } diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index 62eff0d731d..bccf06bcc18 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -18,9 +18,15 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { } } -/// This macro adds a linked module by module, raw_module or inline_js attribute. -/// It expands to a String containing a URL to that module. This URL can be used -/// to create workers or worklets, for example: +/// This macro takes a JS module as input and returns a URL that can be used to +/// access it at runtime. +/// +/// The module can be specified in a few ways: +/// - You can use `inline_js = "..."` to create an inline JS file. +/// - You can use `module = "/foo/bar"` to reference a file relative to the +/// root of the crate the macro is invoked in. +/// +/// The returned URL can be used for things like creating workers/worklets: /// ```no_run /// use web_sys::Worker; /// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js")); From 4f4b0ac6d076d90aef7a5ebfeca35ef3b1539c63 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Fri, 27 Jan 2023 01:57:02 +0100 Subject: [PATCH 17/26] Opt-in to linked modules in wasm-bindgen with `--allow-links` --- crates/cli-support/src/js/mod.rs | 41 ++++++++++++------- crates/cli-support/src/lib.rs | 7 ++++ crates/cli-support/src/wit/mod.rs | 22 ++++++---- crates/cli-support/src/wit/nonstandard.rs | 4 +- crates/cli-support/src/wit/section.rs | 2 +- crates/cli/src/bin/wasm-bindgen.rs | 5 ++- .../futures/src/task/wait_async_polyfill.rs | 11 ++++- crates/futures/src/task/worker.js | 6 --- 8 files changed, 66 insertions(+), 32 deletions(-) delete mode 100644 crates/futures/src/task/worker.js diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index e25f1c316df..12d565b8372 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3118,23 +3118,36 @@ impl<'a> Context<'a> { self.invoke_intrinsic(intrinsic, args, prelude) } - AuxImport::LinkTo(path) => { + AuxImport::LinkTo(path, content) => { assert!(kind == AdapterJsImportKind::Normal); assert!(!variadic); assert_eq!(args.len(), 0); - let base = match self.config.mode { - OutputMode::Web - | OutputMode::Bundler { .. } - | OutputMode::Deno - | OutputMode::Node { - experimental_modules: true, - } => "import.meta.url", - OutputMode::Node { - experimental_modules: false, - } => "require('url').pathToFileURL(__filename)", - OutputMode::NoModules { .. } => "script_src", - }; - Ok(format!("new URL('{}', {}).toString()", path, base)) + if self.config.allow_links { + let base = match self.config.mode { + OutputMode::Web + | OutputMode::Bundler { .. } + | OutputMode::Deno + | OutputMode::Node { + experimental_modules: true, + } => "import.meta.url", + OutputMode::Node { + experimental_modules: false, + } => "require('url').pathToFileURL(__filename)", + OutputMode::NoModules { .. } => "script_src", + }; + Ok(format!("new URL('{}', {}).toString()", path, base)) + } else { + if let Some(content) = content { + Ok(format!( + "\"data:application/javascript,\" + encodeURIComponent(`{}`)", + content.replace('`', "\\`") + )) + } else { + Err(anyhow!("wasm-bindgen needs to be invoked with `--allow-links`, because \"{}\" cannot be embedded.\n\ + `--allow-links` is safe with webpack 5 or no bundler at all.\n\ + For other bundlers, ensure they support the `new URL('…', import.meta.url)` syntax.", path)) + } + } } } } diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index e77137c941a..f487c2bf461 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -45,6 +45,7 @@ pub struct Bindgen { multi_value: bool, wasm_interface_types: bool, encode_into: EncodeInto, + allow_links: bool, } pub struct Output { @@ -118,6 +119,7 @@ impl Bindgen { wasm_interface_types, encode_into: EncodeInto::Test, omit_default_module_path: true, + allow_links: false, } } @@ -297,6 +299,11 @@ impl Bindgen { self } + pub fn allow_links(&mut self, allow_links: bool) -> &mut Bindgen { + self.allow_links = allow_links; + self + } + pub fn generate>(&mut self, path: P) -> Result<(), Error> { self.generate_output()?.emit(path.as_ref()) } diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 88c6a898bd1..133f0cf4b7c 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -336,6 +336,7 @@ impl<'a> Context<'a> { id: ImportId, module: &decode::ImportModule, offset: usize, + inline_js: &[&str], ) -> Result<(), Error> { let descriptor = Function { shim_idx: 0, @@ -344,18 +345,23 @@ impl<'a> Context<'a> { inner_ret: None, }; let id = self.import_adapter(id, descriptor, AdapterJsImportKind::Normal)?; - let path = match module { - decode::ImportModule::Named(n) => format!("snippets/{}", n), - decode::ImportModule::RawNamed(n) => n.to_string(), - decode::ImportModule::Inline(idx) => { + let (path, content) = match module { + decode::ImportModule::Named(n) => (format!("snippets/{}", n), None), + decode::ImportModule::RawNamed(n) => (n.to_string(), None), + decode::ImportModule::Inline(idx) => ( format!( "snippets/{}/inline{}.js", self.unique_crate_identifier, *idx as usize + offset - ) - } + ), + Some(inline_js[*idx as usize]), + ), }; - self.aux.import_map.insert(id, AuxImport::LinkTo(path)); + self.aux.import_map.insert( + id, + // content is embedded as data URI, so it should not be too long. + AuxImport::LinkTo(path, content.filter(|x| x.len() <= 512).map(str::to_string)), + ); Ok(()) } @@ -401,7 +407,7 @@ impl<'a> Context<'a> { .unwrap_or(0); for module in linked_modules { match self.function_imports.remove(module.link_function_name) { - Some((id, _)) => self.link_module(id, &module.module, offset)?, + Some((id, _)) => self.link_module(id, &module.module, offset, &inline_js[..])?, None => (), } } diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index 5b997175a2a..4c7eb523555 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -331,7 +331,9 @@ pub enum AuxImport { /// This is a function which returns a URL pointing to a specific file, /// usually a JS snippet. The supplied path is relative to the JS glue shim. - LinkTo(String), + /// The Option may contain the contents of the linked file, so it can be + /// embedded. + LinkTo(String, Option), } /// Values that can be imported verbatim to hook up to an import. diff --git a/crates/cli-support/src/wit/section.rs b/crates/cli-support/src/wit/section.rs index b660dddf738..bd1fb23a2e7 100644 --- a/crates/cli-support/src/wit/section.rs +++ b/crates/cli-support/src/wit/section.rs @@ -355,7 +355,7 @@ fn check_standard_import(import: &AuxImport) -> Result<(), Error> { AuxImport::Intrinsic(intrinsic) => { format!("wasm-bindgen specific intrinsic `{}`", intrinsic.name()) } - AuxImport::LinkTo(path) => { + AuxImport::LinkTo(path, _) => { format!("wasm-bindgen specific link function for `{}`", path) } AuxImport::Closure { .. } => format!("creating a `Closure` wrapper"), diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index 877b0c41a12..ab79bf03dfe 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -35,6 +35,7 @@ Options: --remove-name-section Remove the debugging `name` section of the file --remove-producers-section Remove the telemetry `producers` section --omit-default-module-path Don't add WebAssembly fallback imports in generated JavaScript + --allow-links Allow to use the `new URL('…', import.meta.url)` syntax --encode-into MODE Whether or not to use TextEncoder#encodeInto, valid values are [test, always, never] --nodejs Deprecated, use `--target nodejs` @@ -68,6 +69,7 @@ struct Args { flag_encode_into: Option, flag_target: Option, flag_omit_default_module_path: bool, + flag_allow_links: bool, arg_input: Option, } @@ -120,7 +122,8 @@ fn rmain(args: &Args) -> Result<(), Error> { .remove_producers_section(args.flag_remove_producers_section) .typescript(typescript) .omit_imports(args.flag_omit_imports) - .omit_default_module_path(args.flag_omit_default_module_path); + .omit_default_module_path(args.flag_omit_default_module_path) + .allow_links(args.flag_allow_links); if let Some(true) = args.flag_weak_refs { b.weak_refs(true); } diff --git a/crates/futures/src/task/wait_async_polyfill.rs b/crates/futures/src/task/wait_async_polyfill.rs index 3e5efe813cb..749c93394b0 100644 --- a/crates/futures/src/task/wait_async_polyfill.rs +++ b/crates/futures/src/task/wait_async_polyfill.rs @@ -53,7 +53,16 @@ fn alloc_helper() -> Worker { return helper; } - let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js"); + let worker_url = wasm_bindgen::link_to!( + inline_js = " +onmessage = function (ev) { + let [ia, index, value] = ev.data; + ia = new Int32Array(ia.buffer); + let result = Atomics.wait(ia, index, value); + postMessage(result); +}; +" + ); Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) }) } diff --git a/crates/futures/src/task/worker.js b/crates/futures/src/task/worker.js deleted file mode 100644 index d25dab6606d..00000000000 --- a/crates/futures/src/task/worker.js +++ /dev/null @@ -1,6 +0,0 @@ -onmessage = function (ev) { - let [ia, index, value] = ev.data; - ia = new Int32Array(ia.buffer); - let result = Atomics.wait(ia, index, value); - postMessage(result); -}; From 79ad0fdd2041f5348f64d2d6a416981a15e8713d Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Fri, 27 Jan 2023 03:31:08 +0100 Subject: [PATCH 18/26] Fix tests --- crates/cli-support/src/js/mod.rs | 2 +- crates/cli-support/src/lib.rs | 2 +- crates/macro/ui-tests/link-to.stderr | 12 ++++++------ crates/shared/src/schema_hash_approval.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 1b9d072315a..60b8b11faf8 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -384,7 +384,7 @@ impl<'a> Context<'a> { if (typeof document === 'undefined') { script_src = location.href; } else { - script_src = document.currentScript.src; + script_src = new URL(document.currentScript.src, location.href).toString(); }\n", ); js.push_str("let wasm;\n"); diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 5481bba06f2..6cecf10d06d 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -121,7 +121,7 @@ impl Bindgen { wasm_interface_types, encode_into: EncodeInto::Test, omit_default_module_path: true, - allow_links: false, + allow_links: true, } } diff --git a/crates/macro/ui-tests/link-to.stderr b/crates/macro/ui-tests/link-to.stderr index 00565390549..61aaec7830f 100644 --- a/crates/macro/ui-tests/link-to.stderr +++ b/crates/macro/ui-tests/link-to.stderr @@ -1,23 +1,23 @@ error: `link_to!` does not support module paths. - --> $DIR/link-to.rs:10:37 + --> ui-tests/link-to.rs:10:37 | 10 | wasm_bindgen::link_to!(module = "package/foo.js") | ^^^^^^^^^^^^^^^^ error: `link_to!` does not support module paths. - --> $DIR/link-to.rs:14:41 + --> ui-tests/link-to.rs:14:41 | 14 | wasm_bindgen::link_to!(raw_module = "package/foo.js") | ^^^^^^^^^^^^^^^^ -error: failed to read file `$WORKSPACE/target/tests/wasm-bindgen-macro/src/not-found.js`: No such file or directory (os error 2) - --> $DIR/link-to.rs:18:37 +error: failed to read file `$WORKSPACE/target/tests/trybuild/wasm-bindgen-macro/src/not-found.js`: No such file or directory (os error 2) + --> ui-tests/link-to.rs:18:37 | 18 | wasm_bindgen::link_to!(module = "/src/not-found.js") | ^^^^^^^^^^^^^^^^^^^ error: `link_to!` requires a module. - --> $DIR/link-to.rs:22:5 + --> ui-tests/link-to.rs:22:5 | 22 | wasm_bindgen::link_to!() | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ error: `link_to!` requires a module. = note: this error originates in the macro `wasm_bindgen::link_to` (in Nightly builds, run with -Z macro-backtrace for more info) error: unused wasm_bindgen attribute - --> $DIR/link-to.rs:28:9 + --> ui-tests/link-to.rs:28:9 | 28 | js_namespace = foo | ^^^^^^^^^^^^ diff --git a/crates/shared/src/schema_hash_approval.rs b/crates/shared/src/schema_hash_approval.rs index 25084e90566..0d4d0bb38f1 100644 --- a/crates/shared/src/schema_hash_approval.rs +++ b/crates/shared/src/schema_hash_approval.rs @@ -8,7 +8,7 @@ // If the schema in this library has changed then: // 1. Bump the version in `crates/shared/Cargo.toml` // 2. Change the `SCHEMA_VERSION` in this library to this new Cargo.toml version -const APPROVED_SCHEMA_FILE_HASH: &'static str = "1473650450341157828"; +const APPROVED_SCHEMA_FILE_HASH: &'static str = "584864585234329974"; #[test] fn schema_version() { From 1edb30df01fa5af740c926504c7f1e44928dcb94 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Fri, 27 Jan 2023 04:20:54 +0100 Subject: [PATCH 19/26] Support embedding for local modules --- crates/cli-support/src/wit/mod.rs | 20 ++++++++++++++++--- .../futures/src/task/wait_async_polyfill.rs | 11 +--------- crates/futures/src/task/worker.js | 6 ++++++ 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 crates/futures/src/task/worker.js diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 8554534b414..93829cf06c9 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -1,3 +1,4 @@ +use crate::decode::LocalModule; use crate::descriptor::{Descriptor, Function}; use crate::descriptors::WasmBindgenDescriptorsSection; use crate::intrinsic::Intrinsic; @@ -345,6 +346,7 @@ impl<'a> Context<'a> { id: ImportId, module: &decode::ImportModule, offset: usize, + local_modules: &[LocalModule], inline_js: &[&str], ) -> Result<(), Error> { let descriptor = Function { @@ -355,7 +357,13 @@ impl<'a> Context<'a> { }; let id = self.import_adapter(id, descriptor, AdapterJsImportKind::Normal)?; let (path, content) = match module { - decode::ImportModule::Named(n) => (format!("snippets/{}", n), None), + decode::ImportModule::Named(n) => ( + format!("snippets/{}", n), + local_modules + .iter() + .find(|m| m.identifier == *n) + .map(|m| m.contents), + ), decode::ImportModule::RawNamed(n) => (n.to_string(), None), decode::ImportModule::Inline(idx) => ( format!( @@ -389,7 +397,7 @@ impl<'a> Context<'a> { linked_modules, } = program; - for module in local_modules { + for module in &local_modules { // All local modules we find should be unique, but the same module // may have showed up in a few different blocks. If that's the case // all the same identifiers should have the same contents. @@ -416,7 +424,13 @@ impl<'a> Context<'a> { .unwrap_or(0); for module in linked_modules { match self.function_imports.remove(module.link_function_name) { - Some((id, _)) => self.link_module(id, &module.module, offset, &inline_js[..])?, + Some((id, _)) => self.link_module( + id, + &module.module, + offset, + &local_modules[..], + &inline_js[..], + )?, None => (), } } diff --git a/crates/futures/src/task/wait_async_polyfill.rs b/crates/futures/src/task/wait_async_polyfill.rs index ebc3e245d5c..68332d91a57 100644 --- a/crates/futures/src/task/wait_async_polyfill.rs +++ b/crates/futures/src/task/wait_async_polyfill.rs @@ -52,16 +52,7 @@ fn alloc_helper() -> Worker { return helper; } - let worker_url = wasm_bindgen::link_to!( - inline_js = " -onmessage = function (ev) { - let [ia, index, value] = ev.data; - ia = new Int32Array(ia.buffer); - let result = Atomics.wait(ia, index, value); - postMessage(result); -}; -" - ); + let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js"); Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) }) } diff --git a/crates/futures/src/task/worker.js b/crates/futures/src/task/worker.js new file mode 100644 index 00000000000..d25dab6606d --- /dev/null +++ b/crates/futures/src/task/worker.js @@ -0,0 +1,6 @@ +onmessage = function (ev) { + let [ia, index, value] = ev.data; + ia = new Int32Array(ia.buffer); + let result = Atomics.wait(ia, index, value); + postMessage(result); +}; From 4d95cf7f1d9117398667d04efe28df603ffa5130 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 30 Jan 2023 01:32:43 +0100 Subject: [PATCH 20/26] Remove linked module embed limit --- crates/cli-support/src/wit/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index 93829cf06c9..7986afb1c3a 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -374,11 +374,9 @@ impl<'a> Context<'a> { Some(inline_js[*idx as usize]), ), }; - self.aux.import_map.insert( - id, - // content is embedded as data URI, so it should not be too long. - AuxImport::LinkTo(path, content.filter(|x| x.len() <= 512).map(str::to_string)), - ); + self.aux + .import_map + .insert(id, AuxImport::LinkTo(path, content.map(str::to_string))); Ok(()) } From a78f1ec3a218f1c2caf1881e1a1c5f2f860c9efd Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 30 Jan 2023 02:06:23 +0100 Subject: [PATCH 21/26] Fix escaping --- crates/cli-support/src/js/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 60b8b11faf8..46d43f987d5 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3164,9 +3164,13 @@ impl<'a> Context<'a> { Ok(format!("new URL('{}', {}).toString()", path, base)) } else { if let Some(content) = content { + let mut escaped = String::with_capacity(content.len()); + content.chars().for_each(|c| match c { + '`' | '\\' | '$' => escaped.extend(['\\', c]), + _ => escaped.extend([c]), + }); Ok(format!( - "\"data:application/javascript,\" + encodeURIComponent(`{}`)", - content.replace('`', "\\`") + "\"data:application/javascript,\" + encodeURIComponent(`{escaped}`)" )) } else { Err(anyhow!("wasm-bindgen needs to be invoked with `--allow-links`, because \"{}\" cannot be embedded.\n\ From 39bec64bca7f5ad0c39f5924144dcc6ea9e6c840 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 30 Jan 2023 12:31:05 +0100 Subject: [PATCH 22/26] Add and refer to the documentation --- crates/cli-support/src/js/mod.rs | 3 +-- guide/src/reference/cli.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 46d43f987d5..a08aee93eea 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3174,8 +3174,7 @@ impl<'a> Context<'a> { )) } else { Err(anyhow!("wasm-bindgen needs to be invoked with `--allow-links`, because \"{}\" cannot be embedded.\n\ - `--allow-links` is safe with webpack 5 or no bundler at all.\n\ - For other bundlers, ensure they support the `new URL('…', import.meta.url)` syntax.", path)) + See https://rustwasm.github.io/wasm-bindgen/reference/cli.html#--allow-links for details.", path)) } } } diff --git a/guide/src/reference/cli.md b/guide/src/reference/cli.md index 9c26649606f..9f77eb6f726 100644 --- a/guide/src/reference/cli.md +++ b/guide/src/reference/cli.md @@ -103,4 +103,14 @@ about reference types](./reference-types.md). ### `--omit-default-module-path` -Don't add WebAssembly fallback imports in generated JavaScript. \ No newline at end of file +Don't add WebAssembly fallback imports in generated JavaScript. + +### `--allow-links` + +Allow to use the `new URL('…', import.meta.url)` link syntax. This is safe with +webpack 5 or no bundler at all. + +For other bundlers, ensure they support the link syntax, possibly by enabling an +extra plugin. Alternatively, configure the bundler to keep the link syntax as is +and to copy all files in `snippets/` to the output directory preserving their +paths. \ No newline at end of file From e2f942c20682e7c77f137af5068f673819f17aec Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Mon, 30 Jan 2023 15:38:34 +0100 Subject: [PATCH 23/26] Rename option and improve documentation --- crates/cli-support/src/js/mod.rs | 6 +++--- crates/cli-support/src/lib.rs | 8 ++++---- crates/cli/src/bin/wasm-bindgen.rs | 9 ++++++--- guide/src/reference/cli.md | 16 ++++++++++------ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index a08aee93eea..5f0cb08abe3 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -3148,7 +3148,7 @@ impl<'a> Context<'a> { assert!(kind == AdapterJsImportKind::Normal); assert!(!variadic); assert_eq!(args.len(), 0); - if self.config.allow_links { + if self.config.split_linked_modules { let base = match self.config.mode { OutputMode::Web | OutputMode::Bundler { .. } @@ -3173,8 +3173,8 @@ impl<'a> Context<'a> { "\"data:application/javascript,\" + encodeURIComponent(`{escaped}`)" )) } else { - Err(anyhow!("wasm-bindgen needs to be invoked with `--allow-links`, because \"{}\" cannot be embedded.\n\ - See https://rustwasm.github.io/wasm-bindgen/reference/cli.html#--allow-links for details.", path)) + Err(anyhow!("wasm-bindgen needs to be invoked with `--split-linked-modules`, because \"{}\" cannot be embedded.\n\ + See https://rustwasm.github.io/wasm-bindgen/reference/cli.html#--split-linked-modules for details.", path)) } } } diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 6cecf10d06d..1f8b446c2dd 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -46,7 +46,7 @@ pub struct Bindgen { multi_value: bool, wasm_interface_types: bool, encode_into: EncodeInto, - allow_links: bool, + split_linked_modules: bool, } pub struct Output { @@ -121,7 +121,7 @@ impl Bindgen { wasm_interface_types, encode_into: EncodeInto::Test, omit_default_module_path: true, - allow_links: true, + split_linked_modules: true, } } @@ -306,8 +306,8 @@ impl Bindgen { self } - pub fn allow_links(&mut self, allow_links: bool) -> &mut Bindgen { - self.allow_links = allow_links; + pub fn split_linked_modules(&mut self, split_linked_modules: bool) -> &mut Bindgen { + self.split_linked_modules = split_linked_modules; self } diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index 160b45a4af2..f6e16a4fb8e 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -36,7 +36,8 @@ Options: --remove-name-section Remove the debugging `name` section of the file --remove-producers-section Remove the telemetry `producers` section --omit-default-module-path Don't add WebAssembly fallback imports in generated JavaScript - --allow-links Allow to use the `new URL('…', import.meta.url)` syntax + --split-linked-modules Split linked modules out into their own files. Recommended if possible. + If a bundler is used, it needs to be set up accordingly. --encode-into MODE Whether or not to use TextEncoder#encodeInto, valid values are [test, always, never] --nodejs Deprecated, use `--target nodejs` @@ -45,6 +46,8 @@ Options: --weak-refs Enable usage of the JS weak references proposal --reference-types Enable usage of WebAssembly reference types -V --version Print the version number of wasm-bindgen + +Additional documentation: https://rustwasm.github.io/wasm-bindgen/reference/cli.html "; #[derive(Debug, Deserialize)] @@ -71,7 +74,7 @@ struct Args { flag_encode_into: Option, flag_target: Option, flag_omit_default_module_path: bool, - flag_allow_links: bool, + flag_split_linked_modules: bool, arg_input: Option, } @@ -126,7 +129,7 @@ fn rmain(args: &Args) -> Result<(), Error> { .typescript(typescript) .omit_imports(args.flag_omit_imports) .omit_default_module_path(args.flag_omit_default_module_path) - .allow_links(args.flag_allow_links); + .split_linked_modules(args.flag_split_linked_modules); if let Some(true) = args.flag_weak_refs { b.weak_refs(true); } diff --git a/guide/src/reference/cli.md b/guide/src/reference/cli.md index 9f77eb6f726..b4dc2ea04bf 100644 --- a/guide/src/reference/cli.md +++ b/guide/src/reference/cli.md @@ -105,12 +105,16 @@ about reference types](./reference-types.md). Don't add WebAssembly fallback imports in generated JavaScript. -### `--allow-links` +### `--split-linked-modules` -Allow to use the `new URL('…', import.meta.url)` link syntax. This is safe with -webpack 5 or no bundler at all. +Controls whether wasm-bindgen will split linked modules out into their own files. +Enabling this is recommended, because it allows lazy loading and setting a +stricter Content Security Policy. + +wasm-bindgen uses the `new URL('…', import.meta.url)` syntax to link such split +out files. This is directly supported when using webpack 5 or no bundler at all. For other bundlers, ensure they support the link syntax, possibly by enabling an -extra plugin. Alternatively, configure the bundler to keep the link syntax as is -and to copy all files in `snippets/` to the output directory preserving their -paths. \ No newline at end of file +extra plugin. That's why this option is disabled by default. Alternatively, +configure the bundler to keep the link syntax as is and to copy all files in +`snippets/` to the output directory preserving their paths. From 44450065da5c5b5c23a5289c52cf5202fb8b5669 Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 31 Jan 2023 16:16:27 +0100 Subject: [PATCH 24/26] Improve documentation --- guide/src/reference/cli.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/guide/src/reference/cli.md b/guide/src/reference/cli.md index b4dc2ea04bf..538d839ca4b 100644 --- a/guide/src/reference/cli.md +++ b/guide/src/reference/cli.md @@ -107,14 +107,16 @@ Don't add WebAssembly fallback imports in generated JavaScript. ### `--split-linked-modules` -Controls whether wasm-bindgen will split linked modules out into their own files. -Enabling this is recommended, because it allows lazy loading and setting a -stricter Content Security Policy. - -wasm-bindgen uses the `new URL('…', import.meta.url)` syntax to link such split -out files. This is directly supported when using webpack 5 or no bundler at all. - -For other bundlers, ensure they support the link syntax, possibly by enabling an -extra plugin. That's why this option is disabled by default. Alternatively, -configure the bundler to keep the link syntax as is and to copy all files in -`snippets/` to the output directory preserving their paths. +Controls whether wasm-bindgen will split linked modules out into their own +files. Enabling this is recommended, because it allows lazy-loading the linked +modules and setting a stricter Content Security Policy. + +wasm-bindgen uses the `new URL('…', import.meta.url)` syntax to resolve the +links to such split out files. This breaks with most bundlers, since the +bundler doesn't know to include the linked module in its output. That's why +this option is disabled by default. Webpack 5 is an exception, which has +special treatment for that syntax. For other bundlers, you'll need to take +extra steps to get it to work, likely by using a plugin. Alternatively, you can +leave the syntax as is and instead manually configure the bundler to copy all +files in `snippets/` to the output directory, preserving their paths relative +to whichever bundled file ends up containing the JS shim. From 319c747c163a2cd6f367daf4dc390f84ac1aedba Mon Sep 17 00:00:00 2001 From: Lukas Lihotzki Date: Tue, 31 Jan 2023 18:07:41 +0100 Subject: [PATCH 25/26] Update crates/macro/src/lib.rs Co-authored-by: Liam Murphy --- crates/macro/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index bccf06bcc18..fbae818750e 100644 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -40,6 +40,9 @@ pub fn link_to(input: TokenStream) -> TokenStream { } tokens.into() } + // This `String::clone` is here so that IDEs know this is supposed to be a + // `String` and can keep type-checking the rest of the program even if the macro + // fails. Err(diagnostic) => (quote! { String::clone(#diagnostic) }).into(), } } From 944dde68142ca57913f7f0baa7593952b6f0aaa1 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Wed, 1 Feb 2023 09:48:01 +1100 Subject: [PATCH 26/26] Add paragraph break in docs --- guide/src/reference/cli.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/guide/src/reference/cli.md b/guide/src/reference/cli.md index 538d839ca4b..d08e066361c 100644 --- a/guide/src/reference/cli.md +++ b/guide/src/reference/cli.md @@ -112,11 +112,13 @@ files. Enabling this is recommended, because it allows lazy-loading the linked modules and setting a stricter Content Security Policy. wasm-bindgen uses the `new URL('…', import.meta.url)` syntax to resolve the -links to such split out files. This breaks with most bundlers, since the -bundler doesn't know to include the linked module in its output. That's why -this option is disabled by default. Webpack 5 is an exception, which has -special treatment for that syntax. For other bundlers, you'll need to take -extra steps to get it to work, likely by using a plugin. Alternatively, you can -leave the syntax as is and instead manually configure the bundler to copy all -files in `snippets/` to the output directory, preserving their paths relative -to whichever bundled file ends up containing the JS shim. +links to such split out files. This breaks with most bundlers, since the bundler +doesn't know to include the linked module in its output. That's why this option +is disabled by default. Webpack 5 is an exception, which has special treatment +for that syntax. + +For other bundlers, you'll need to take extra steps to get it to work, likely by +using a plugin. Alternatively, you can leave the syntax as is and instead +manually configure the bundler to copy all files in `snippets/` to the output +directory, preserving their paths relative to whichever bundled file ends up +containing the JS shim.