diff --git a/crates/cli-support/src/js/js2rust.rs b/crates/cli-support/src/js/js2rust.rs index d300e79e..ce8b67ea 100644 --- a/crates/cli-support/src/js/js2rust.rs +++ b/crates/cli-support/src/js/js2rust.rs @@ -23,10 +23,6 @@ pub struct Js2Rust<'a, 'b: 'a> { /// finishes. This is scheduled in a `finally` block. finally: String, - /// Next global index to write to when passing arguments via the single - /// global stack. - global_idx: usize, - /// Index of the next argument for unique name generation purposes. arg_idx: usize, @@ -52,7 +48,6 @@ impl<'a, 'b> Js2Rust<'a, 'b> { js_arguments: Vec::new(), prelude: String::new(), finally: String::new(), - global_idx: 0, arg_idx: 0, ret_ty: String::new(), ret_expr: String::new(), @@ -102,21 +97,23 @@ impl<'a, 'b> Js2Rust<'a, 'b> { self } + fn abi_arg(&mut self) -> String { + let s = format!("arg{}", self.arg_idx); + self.arg_idx += 1; + s + } + pub fn argument(&mut self, arg: &Descriptor) -> Result<&mut Self, Error> { let i = self.arg_idx; - self.arg_idx += 1; - let name = format!("arg{}", i); + let name = self.abi_arg(); if let Some(kind) = arg.vector_kind() { self.js_arguments.push((name.clone(), kind.js_ty().to_string())); let func = self.cx.pass_to_wasm_function(kind)?; - self.cx.expose_set_global_argument()?; - let global_idx = self.global_idx(); self.prelude(&format!("\ const [ptr{i}, len{i}] = {func}({arg});\n\ - setGlobalArgument(len{i}, {global_idx});\n\ - ", i = i, func = func, arg = name, global_idx = global_idx)); + ", i = i, func = func, arg = name)); if arg.is_by_ref() { if arg.is_mut_ref() { let get = self.cx.memview_function(kind); @@ -133,6 +130,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> { self.cx.require_internal_export("__wbindgen_free")?; } self.rust_arguments.push(format!("ptr{}", i)); + self.rust_arguments.push(format!("len{}", i)); return Ok(self) } @@ -187,7 +185,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> { _assertBoolean({name});\n\ ", name = name)); } - self.rust_arguments.push(format!("arg{i} ? 1 : 0", i = i)); + self.rust_arguments.push(format!("{} ? 1 : 0", name)); } Descriptor::Anyref => { self.js_arguments.push((name.clone(), "any".to_string())); @@ -225,13 +223,18 @@ impl<'a, 'b> Js2Rust<'a, 'b> { if let Some(ty) = ty.vector_kind() { self.ret_ty = ty.js_ty().to_string(); let f = self.cx.expose_get_vector_from_wasm(ty); - self.cx.expose_get_global_argument()?; + self.cx.expose_global_argument_ptr()?; + self.cx.expose_uint32_memory(); self.cx.require_internal_export("__wbindgen_free")?; + self.prelude("const retptr = globalArgumentPtr();"); + self.rust_arguments.insert(0, "retptr".to_string()); self.ret_expr = format!("\ - const ret = RET;\n\ - const len = getGlobalArgument(0);\n\ - const realRet = {}(ret, len);\n\ - wasm.__wbindgen_free(ret, len * {});\n\ + RET;\n\ + const mem = getUint32Memory();\n\ + const ptr = mem[retptr / 4];\n\ + const len = mem[retptr / 4 + 1];\n\ + const realRet = {}(ptr, len);\n\ + wasm.__wbindgen_free(ptr, len * {});\n\ return realRet;\n\ ", f, ty.size()); return Ok(self) @@ -309,10 +312,4 @@ impl<'a, 'b> Js2Rust<'a, 'b> { let ts = format!("{} {}({}): {};\n", prefix, self.js_name, ts_args, self.ret_ty); (js, ts) } - - fn global_idx(&mut self) -> usize { - let ret = self.global_idx; - self.global_idx += 1; - ret - } } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index f9c84db6..3390d8a0 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1318,21 +1318,6 @@ impl<'a> Context<'a> { } } - fn expose_set_global_argument(&mut self) -> Result<(), Error> { - if !self.exposed_globals.insert("set_global_argument") { - return Ok(()); - } - self.expose_uint32_memory(); - self.expose_global_argument_ptr()?; - self.global(" - function setGlobalArgument(arg, i) { - const idx = globalArgumentPtr() / 4 + i; - getUint32Memory()[idx] = arg; - } - "); - Ok(()) - } - fn expose_get_global_argument(&mut self) -> Result<(), Error> { if !self.exposed_globals.insert("get_global_argument") { return Ok(()); diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index d47369e4..874d2bf0 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -72,31 +72,32 @@ impl<'a, 'b> Rust2Js<'a, 'b> { Ok(self) } - fn argument(&mut self, arg: &Descriptor) -> Result<(), Error> { - let i = self.arg_idx; + fn shim_argument(&mut self) -> String { + let s = format!("arg{}", self.arg_idx); self.arg_idx += 1; + self.shim_arguments.push(s.clone()); + s + } - self.shim_arguments.push(format!("arg{}", i)); + fn argument(&mut self, arg: &Descriptor) -> Result<(), Error> { + let abi = self.shim_argument(); if let Some(ty) = arg.vector_kind() { + let abi2 = self.shim_argument(); let f = self.cx.expose_get_vector_from_wasm(ty); - self.cx.expose_get_global_argument()?; - let next_global = self.global_idx(); - self.prelude(&format!("\ - let len{0} = getGlobalArgument({next_global});\n\ - let v{0} = {func}(arg{0}, len{0});\n\ - ", i, func = f, next_global = next_global)); + self.prelude(&format!("let v{0} = {func}({0}, {1});", + abi, abi2, func = f)); if !arg.is_by_ref() { self.prelude(&format!("\ - wasm.__wbindgen_free(arg{0}, len{0} * {size});\ - ", i, size = ty.size())); + wasm.__wbindgen_free({0}, {1} * {size});\ + ", abi, abi2, size = ty.size())); self.cx.require_internal_export("__wbindgen_free")?; } else if arg.is_mut_ref() { let f = self.cx.expose_commit_slice_to_wasm(ty)?; - self.finally(&format!("{}(arg{i}, v{i});", f, i = i)); + self.finally(&format!("{}({1}, v{1});", f, abi)); } - self.js_arguments.push(format!("v{}", i)); + self.js_arguments.push(format!("v{}", abi)); return Ok(()) } @@ -104,9 +105,9 @@ impl<'a, 'b> Rust2Js<'a, 'b> { if arg.is_by_ref() { bail!("cannot invoke JS functions with custom ref types yet") } - let assign = format!("let c{0} = {1}.__construct(arg{0});", i, class); + let assign = format!("let c{0} = {1}.__construct({0});", abi, class); self.prelude(&assign); - self.js_arguments.push(format!("c{}", i)); + self.js_arguments.push(format!("c{}", abi)); return Ok(()) } @@ -132,12 +133,12 @@ impl<'a, 'b> Rust2Js<'a, 'b> { self.global_idx(); self.prelude(&format!("\ let cb{0} = {js};\n\ - cb{0}.f = wasm.__wbg_function_table.get(arg{0});\n\ + cb{0}.f = wasm.__wbg_function_table.get({0});\n\ cb{0}.a = getGlobalArgument({next_global});\n\ cb{0}.b = getGlobalArgument({next_global} + 1);\n\ - ", i, js = js, next_global = next_global)); - self.finally(&format!("cb{0}.a = cb{0}.b = 0;", i)); - self.js_arguments.push(format!("cb{0}.bind(cb{0})", i)); + ", abi, js = js, next_global = next_global)); + self.finally(&format!("cb{0}.a = cb{0}.b = 0;", abi)); + self.js_arguments.push(format!("cb{0}.bind(cb{0})", abi)); return Ok(()) } @@ -168,35 +169,35 @@ impl<'a, 'b> Rust2Js<'a, 'b> { cb{0}.f = wasm.__wbg_function_table.get(getGlobalArgument({c}));\n\ let real = cb{0}.bind(cb{0});\n\ real.original = cb{0};\n\ - idx{0} = getUint32Memory()[arg{0} / 4] = addHeapObject(real);\n\ + idx{0} = getUint32Memory()[{0} / 4] = addHeapObject(real);\n\ ", - i, + abi, js = js, a = self.global_idx(), b = self.global_idx(), c = self.global_idx(), ); self.prelude(&format!("\ - let idx{0} = getUint32Memory()[arg{0} / 4];\n\ + let idx{0} = getUint32Memory()[{0} / 4];\n\ if (idx{0} === 0xffffffff) {{\n\ {1}\ }}\n\ - ", i, indent(&reset_idx))); + ", abi, indent(&reset_idx))); self.cx.expose_get_object(); - self.js_arguments.push(format!("getObject(idx{})", i)); + self.js_arguments.push(format!("getObject(idx{})", abi)); return Ok(()) } let invoc_arg = match *arg { - ref d if d.is_number() => format!("arg{}", i), - Descriptor::Boolean => format!("arg{} !== 0", i), + ref d if d.is_number() => abi, + Descriptor::Boolean => format!("{} !== 0", abi), Descriptor::Anyref => { self.cx.expose_take_object(); - format!("takeObject(arg{})", i) + format!("takeObject({})", abi) } ref d if d.is_ref_anyref() => { self.cx.expose_get_object(); - format!("getObject(arg{})", i) + format!("getObject({})", abi) } _ => bail!("unimplemented argument type in imported function: {:?}", arg), }; @@ -218,11 +219,12 @@ impl<'a, 'b> Rust2Js<'a, 'b> { if let Some(ty) = ty.vector_kind() { let f = self.cx.pass_to_wasm_function(ty)?; self.cx.expose_uint32_memory(); - self.cx.expose_set_global_argument()?; + self.shim_arguments.insert(0, "ret".to_string()); self.ret_expr = format!("\ const [retptr, retlen] = {}(JS);\n\ - setGlobalArgument(retlen, 0);\n\ - return retptr;\n\ + const mem = getUint32Memory(); + mem[ret / 4] = retptr; + mem[ret / 4 + 1] = retlen; ", f); return Ok(()) } diff --git a/src/convert.rs b/src/convert.rs index 82fb908a..7c19d0b7 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -61,6 +61,14 @@ unsafe impl WasmAbi for i64 {} unsafe impl WasmAbi for f32 {} unsafe impl WasmAbi for f64 {} +#[repr(C)] +pub struct WasmSlice { + pub ptr: u32, + pub len: u32, +} + +unsafe impl WasmAbi for WasmSlice {} + macro_rules! simple { ($($t:tt)*) => ($( impl IntoWasmAbi for $t { @@ -137,67 +145,71 @@ macro_rules! vectors { ($($t:ident)*) => ($( #[cfg(feature = "std")] impl IntoWasmAbi for Box<[$t]> { - type Abi = u32; + type Abi = WasmSlice; - fn into_abi(self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> WasmSlice { let ptr = self.as_ptr(); let len = self.len(); mem::forget(self); - extra.push(len as u32); - ptr.into_abi(extra) + WasmSlice { + ptr: ptr.into_abi(extra), + len: len as u32, + } } } #[cfg(feature = "std")] impl FromWasmAbi for Box<[$t]> { - type Abi = u32; + type Abi = WasmSlice; - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { - let ptr = <*mut $t>::from_abi(js, extra); - let len = extra.pop() as usize; + unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self { + let ptr = <*mut $t>::from_abi(js.ptr, extra); + let len = js.len as usize; Vec::from_raw_parts(ptr, len, len).into_boxed_slice() } } impl<'a> IntoWasmAbi for &'a [$t] { - type Abi = u32; + type Abi = WasmSlice; - fn into_abi(self, extra: &mut Stack) -> u32 { - let ptr = self.as_ptr(); - let len = self.len(); - extra.push(len as u32); - ptr.into_abi(extra) + fn into_abi(self, extra: &mut Stack) -> WasmSlice { + WasmSlice { + ptr: self.as_ptr().into_abi(extra), + len: self.len() as u32, + } } } impl<'a> IntoWasmAbi for &'a mut [$t] { - type Abi = u32; + type Abi = WasmSlice; - fn into_abi(self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> WasmSlice { (&*self).into_abi(extra) } } impl RefFromWasmAbi for [$t] { - type Abi = u32; + type Abi = WasmSlice; type Anchor = &'static [$t]; - unsafe fn ref_from_abi(js: u32, extra: &mut Stack) -> &'static [$t] { + unsafe fn ref_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static [$t] { slice::from_raw_parts( - <*const $t>::from_abi(js, extra), - extra.pop() as usize, + <*const $t>::from_abi(js.ptr, extra), + js.len as usize, ) } } impl RefMutFromWasmAbi for [$t] { - type Abi = u32; + type Abi = WasmSlice; type Anchor = &'static mut [$t]; - unsafe fn ref_mut_from_abi(js: u32, extra: &mut Stack) -> &'static mut [$t] { + unsafe fn ref_mut_from_abi(js: WasmSlice, extra: &mut Stack) + -> &'static mut [$t] + { slice::from_raw_parts_mut( - <*mut $t>::from_abi(js, extra), - extra.pop() as usize, + <*mut $t>::from_abi(js.ptr, extra), + js.len as usize, ) } } @@ -225,17 +237,17 @@ if_std! { } impl IntoWasmAbi for String { - type Abi = u32; + type Abi = as IntoWasmAbi>::Abi; - fn into_abi(self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.into_bytes().into_abi(extra) } } impl FromWasmAbi for String { - type Abi = u32; + type Abi = as FromWasmAbi>::Abi; - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { + unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { String::from_utf8_unchecked(>::from_abi(js, extra)) } } @@ -294,23 +306,25 @@ impl RefFromWasmAbi for JsValue { if_std! { impl IntoWasmAbi for Box<[JsValue]> { - type Abi = u32; + type Abi = WasmSlice; - fn into_abi(self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> WasmSlice { let ptr = self.as_ptr(); let len = self.len(); mem::forget(self); - extra.push(len as u32); - ptr.into_abi(extra) + WasmSlice { + ptr: ptr.into_abi(extra), + len: len as u32, + } } } impl FromWasmAbi for Box<[JsValue]> { - type Abi = u32; + type Abi = WasmSlice; - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { - let ptr = <*mut JsValue>::from_abi(js, extra); - let len = extra.pop() as usize; + unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self { + let ptr = <*mut JsValue>::from_abi(js.ptr, extra); + let len = js.len as usize; Vec::from_raw_parts(ptr, len, len).into_boxed_slice() } } diff --git a/tests/all/imports.rs b/tests/all/imports.rs index e5b9b414..36c6c3d5 100644 --- a/tests/all/imports.rs +++ b/tests/all/imports.rs @@ -113,6 +113,40 @@ fn unused() { .test(); } +#[test] +fn string_ret() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen(module = "./test")] + extern { + fn foo() -> String; + } + + #[wasm_bindgen] + pub fn run() { + assert_eq!(foo(), "bar"); + } + "#) + .file("test.ts", r#" + import * as wasm from "./out"; + + export function foo(): string { + return 'bar'; + } + + export function test() { + wasm.run(); + } + "#) + .test(); +} + #[test] fn strings() { project()