From 6796bc689567bed6a6f9b36e4cb7f0e98ace53cc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 11 Jun 2019 11:39:48 -0700 Subject: [PATCH] Communicate exceptions through global memory Instead of allocating space on the stack and returning a pointer we should be able to use a single global memory location to communicate this error payload information. This shouldn't run into any reentrancy issues since it's only stored just before returning to wasm and it's always read just after returning from wasm. --- crates/backend/src/codegen.rs | 23 +++-------------------- crates/cli-support/src/js/mod.rs | 21 +++++++++------------ crates/cli-support/src/js/rust2js.rs | 8 +------- src/lib.rs | 22 ++++++++++++++++++++++ 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index fc81c1bf..b6eae8c1 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -974,28 +974,12 @@ impl TryToTokens for ast::ImportFunction { } let mut exceptional_ret = quote!(); - let exn_data = if self.catch { - let exn_data = Ident::new("exn_data", Span::call_site()); - let exn_data_ptr = Ident::new("exn_data_ptr", Span::call_site()); - abi_argument_names.push(exn_data_ptr.clone()); - abi_arguments.push(quote! { #exn_data_ptr: *mut u32 }); + if self.catch { convert_ret = quote! { Ok(#convert_ret) }; exceptional_ret = quote! { - if #exn_data[0] == 1 { - return Err( - < - wasm_bindgen::JsValue as wasm_bindgen::convert::FromWasmAbi - >::from_abi(#exn_data[1], &mut wasm_bindgen::convert::GlobalStack::new()) - ) - } + wasm_bindgen::__rt::take_last_exception()?; }; - quote! { - let mut #exn_data = [0; 2]; - let #exn_data_ptr = #exn_data.as_mut_ptr(); - } - } else { - quote!() - }; + } let rust_name = &self.rust_name; let import_name = &self.shim; @@ -1055,7 +1039,6 @@ impl TryToTokens for ast::ImportFunction { #extern_fn unsafe { - #exn_data let #ret_ident = { let mut __stack = wasm_bindgen::convert::GlobalStack::new(); #(#arg_conversions)* diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index dad09461..94793302 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1419,16 +1419,14 @@ impl<'a> Context<'a> { if !self.should_write_global("handle_error") { return Ok(()); } - self.expose_uint32_memory(); + self.require_internal_export("__wbindgen_exn_store")?; if self.config.anyref { self.expose_add_to_anyref_table()?; self.global( " - function handleError(exnptr, e) { + function handleError(e) { const idx = addToAnyrefTable(e); - const view = getUint32Memory(); - view[exnptr / 4] = 1; - view[exnptr / 4 + 1] = idx; + wasm.__wbindgen_exn_store(idx); } ", ); @@ -1436,10 +1434,8 @@ impl<'a> Context<'a> { self.expose_add_heap_object(); self.global( " - function handleError(exnptr, e) { - const view = getUint32Memory(); - view[exnptr / 4] = 1; - view[exnptr / 4 + 1] = addHeapObject(e); + function handleError(e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); } ", ); @@ -2115,7 +2111,7 @@ impl<'a> Context<'a> { fn export_function_table(&mut self) -> Result<(), Error> { if !self.should_write_global("wbg-function-table") { - return Ok(()) + return Ok(()); } let id = match self.module.tables.main_function_table()? { Some(id) => id, @@ -2192,7 +2188,7 @@ impl ExportedClass { field: &str, js: &str, prefix: &str, - ret_ty: &str + ret_ty: &str, ) -> &mut bool { self.contents.push_str(docs); self.contents.push_str(prefix); @@ -2215,7 +2211,8 @@ impl ExportedClass { /// generated output is deterministic and we do so by ensuring that iteration of /// hash maps is consistently sorted. fn sorted_iter(map: &HashMap) -> impl Iterator - where K: Ord, +where + K: Ord, { let mut pairs = map.iter().collect::>(); pairs.sort_by_key(|(k, _)| *k); diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index b2d39ada..097f3413 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -928,7 +928,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { try {{\n\ {} }} catch (e) {{\n\ - handleError(exnptr, e);\n\ + handleError(e);\n\ }}\ ", &invoc @@ -973,12 +973,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> { let mut ret = String::new(); ret.push_str("function("); ret.push_str(&self.shim_arguments.join(", ")); - if self.catch { - if self.shim_arguments.len() > 0 { - ret.push_str(", ") - } - ret.push_str("exnptr"); - } ret.push_str(") {\n"); ret.push_str(&self.prelude); diff --git a/src/lib.rs b/src/lib.rs index f6097f99..0d8ffe5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1067,6 +1067,28 @@ pub mod __rt { pub fn link_mem_intrinsics() { crate::anyref::link_intrinsics(); } + + static mut GLOBAL_EXNDATA: [u32; 2] = [0; 2]; + + #[no_mangle] + pub unsafe extern "C" fn __wbindgen_exn_store(idx: u32) { + assert_eq!(GLOBAL_EXNDATA[0], 0); + GLOBAL_EXNDATA[0] = 1; + GLOBAL_EXNDATA[1] = idx; + } + + pub fn take_last_exception() -> Result<(), super::JsValue> { + unsafe { + let ret = if GLOBAL_EXNDATA[0] == 1 { + Err(super::JsValue:: _new(GLOBAL_EXNDATA[1])) + } else { + Ok(()) + }; + GLOBAL_EXNDATA[0] = 0; + GLOBAL_EXNDATA[1] = 0; + return ret; + } + } } /// A wrapper type around slices and vectors for binding the `Uint8ClampedArray`