diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 2d1f151b389..0270c7429b0 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -53,6 +53,7 @@ pub enum Descriptor { U64, F32, F64, + Array, Boolean, Function(Box), Closure(Box), diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 515027bb019..7e5f1dc3ba2 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -45,7 +45,17 @@ impl InstructionBuilder<'_, '_> { fn _incoming(&mut self, arg: &Descriptor) -> Result<(), Error> { use walrus::ValType as WasmVT; use wit_walrus::ValType as WitVT; + let vector_kind = |arg: &Descriptor| arg.vector_kind().ok_or_else(|| { + format_err!("unsupported argument type for calling Rust function from JS {:?}", arg) + }); match arg { + Descriptor::Array => { + self.instruction( + &[AdapterType::Vector(vector_kind(arg)?)], + Instruction::I32FromAnyrefOwned, + &[AdapterType::I32], + ); + } Descriptor::Boolean => { self.instruction( &[AdapterType::Bool], @@ -117,9 +127,7 @@ impl InstructionBuilder<'_, '_> { } Descriptor::Vector(_) => { - let kind = arg.vector_kind().ok_or_else(|| { - format_err!("unsupported argument type for calling Rust function from JS {:?}", arg) - })?; + let kind = vector_kind(arg)?; self.instruction( &[AdapterType::Vector(kind)], Instruction::VectorToMemory { diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 69d15c31b11..c0a920e04d7 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -24,7 +24,17 @@ impl InstructionBuilder<'_, '_> { } fn _outgoing(&mut self, arg: &Descriptor) -> Result<(), Error> { + let vector_kind = |arg: &Descriptor| arg.vector_kind().ok_or_else(|| { + format_err!("unsupported argument type for calling JS function from Rust {:?}", arg) + }); match arg { + Descriptor::Array => { + self.instruction( + &[AdapterType::I32], + Instruction::I32FromAnyrefOwned, + &[AdapterType::Vector(vector_kind(arg)?)], + ); + } Descriptor::Boolean => { self.instruction( &[AdapterType::I32], @@ -129,12 +139,7 @@ impl InstructionBuilder<'_, '_> { } Descriptor::Vector(_) => { - let kind = arg.vector_kind().ok_or_else(|| { - format_err!( - "unsupported argument type for calling JS function from Rust {:?}", - arg - ) - })?; + let kind = vector_kind(arg)?; let mem = self.cx.memory()?; let free = self.cx.free()?; self.instruction( diff --git a/examples/arrays/Cargo.toml b/examples/arrays/Cargo.toml new file mode 100644 index 00000000000..499a2c5c7fb --- /dev/null +++ b/examples/arrays/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "arrays" +version = "0.1.0" +authors = ["The wasm-bindgen Developers"] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2.58" diff --git a/examples/arrays/README.md b/examples/arrays/README.md new file mode 100644 index 00000000000..b00e510bc00 --- /dev/null +++ b/examples/arrays/README.md @@ -0,0 +1,15 @@ +# Working with arrays (up to 32) + +[View documentation for this example online][dox] or [View compiled example +online][compiled] + +[compiled]: https://rustwasm.github.io/wasm-bindgen/exbuild/arrays/ +[dox]: https://rustwasm.github.io/docs/wasm-bindgen/examples/arrays.html + +You can build the example locally with: + +``` +$ npm run serve +``` + +and then visiting http://localhost:8080 in a browser should run the example! diff --git a/examples/arrays/index.js b/examples/arrays/index.js new file mode 100644 index 00000000000..a9a161292bb --- /dev/null +++ b/examples/arrays/index.js @@ -0,0 +1,7 @@ +const rust = import('./pkg'); +rust + .then(m => { + console.log(m.asceding_array(10)); + console.log(m.product([1, 2, 3, 4])); + }) + .catch(console.error); diff --git a/examples/arrays/package.json b/examples/arrays/package.json new file mode 100644 index 00000000000..ea4d9490a8e --- /dev/null +++ b/examples/arrays/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "build": "webpack", + "serve": "webpack-dev-server" + }, + "devDependencies": { + "@wasm-tool/wasm-pack-plugin": "1.0.1", + "html-webpack-plugin": "^3.2.0", + "text-encoding": "^0.7.0", + "webpack-cli": "^3.1.1", + "webpack-dev-server": "^3.1.0", + "webpack": "^4.29.4" + } +} diff --git a/examples/arrays/src/lib.rs b/examples/arrays/src/lib.rs new file mode 100644 index 00000000000..98c4b9c61be --- /dev/null +++ b/examples/arrays/src/lib.rs @@ -0,0 +1,13 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn asceding_array(start: i32) -> [i32; 8] { + let mut array = [0; 4]; + array.iter_mut().enumerate().for_each(|(idx, value)| *value = start + idx); + array +} + +#[wasm_bindgen] +pub fn product(from_js: [i32; 4]) -> i32 { + from_js.iter().product() +} \ No newline at end of file diff --git a/examples/arrays/webpack.config.js b/examples/arrays/webpack.config.js new file mode 100644 index 00000000000..a6f6e1e930d --- /dev/null +++ b/examples/arrays/webpack.config.js @@ -0,0 +1,25 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const webpack = require('webpack'); +const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); + +module.exports = { + entry: './index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + }, + plugins: [ + new HtmlWebpackPlugin(), + new WasmPackPlugin({ + crateDirectory: path.resolve(__dirname, ".") + }), + // Have this example work in Edge which doesn't ship `TextEncoder` or + // `TextDecoder` at this time. + new webpack.ProvidePlugin({ + TextDecoder: ['text-encoding', 'TextDecoder'], + TextEncoder: ['text-encoding', 'TextEncoder'] + }) + ], + mode: 'development' +}; diff --git a/src/convert/slices.rs b/src/convert/slices.rs index 0c0fc6267f8..cb00855b8b5 100644 --- a/src/convert/slices.rs +++ b/src/convert/slices.rs @@ -271,3 +271,53 @@ if_std! { fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 } } } + + +macro_rules! array_impls { + ($($N:expr),+) => { + $( + unsafe impl WasmAbi for [T; $N] {} + + impl FromWasmAbi for [T; $N] + where + T: Copy + FromWasmAbi + { + type Abi = [T; $N]; + + #[inline] + unsafe fn from_abi(js: Self::Abi) -> Self { + use core::convert::TryInto; + let slice = slice::from_raw_parts( + <*const T>::from_abi(js.as_ptr() as u32), + js.len(), + ); + slice.try_into().unwrap() + } + } + + impl IntoWasmAbi for [T; $N] + where + T: Copy + IntoWasmAbi + { + type Abi = [T; $N]; + + #[inline] + fn into_abi(self) -> Self::Abi { + unsafe { + use core::convert::TryInto; + let slice = slice::from_raw_parts( + self.as_ptr(), + self.len() + ); + slice.try_into().unwrap() + } + } + } + )+ + } +} + +array_impls!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32 +); \ No newline at end of file diff --git a/src/describe.rs b/src/describe.rs index eec05bd4dbf..ea5aae8c293 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -27,6 +27,7 @@ tys! { U64 F32 F64 + ARRAY BOOLEAN FUNCTION CLOSURE @@ -184,3 +185,21 @@ impl WasmDescribe for Clamped { T::describe(); } } + +macro_rules! array_impls { + ($($N:expr),+) => { + $( + impl WasmDescribe for [T; $N] { + fn describe() { + inform(ARRAY); + T::describe(); + } + } + )+ + } +} + +array_impls!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32 +); \ No newline at end of file diff --git a/tests/wasm/arrays.js b/tests/wasm/arrays.js new file mode 100644 index 00000000000..12c2bb0457f --- /dev/null +++ b/tests/wasm/arrays.js @@ -0,0 +1,22 @@ +const wasm = require('wasm-bindgen-test.js'); +const assert = require('assert'); + +exports.js_ascending_array = () => { + for (let a = 0; a < 33; a ++) { + assert.strictEqual( + wasm["asceding_array" + a](), + [...Array(a).keys()] + ); + } +}; + +exports.js_product = () => { + let array = []; + for (let a = 0; a < 33; a ++) { + assert.strictEqual( + wasm["product" + a](array), + array.reduce((acc, value) => acc * value, 1) + ); + array.push(a); + } +}; diff --git a/tests/wasm/arrays.rs b/tests/wasm/arrays.rs new file mode 100644 index 00000000000..6ff75d9b7e9 --- /dev/null +++ b/tests/wasm/arrays.rs @@ -0,0 +1,72 @@ +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; + +#[wasm_bindgen(module = "tests/wasm/arrays.js")] +extern "C" { + fn js_ascending_array(); + fn js_product(); +} + +#[wasm_bindgen_test] +fn rust_ascending_array() { + js_ascending_array(); +} + +#[wasm_bindgen_test] +fn rust_product() { + js_product(); +} + +macro_rules! array_impls { + ($($N:expr $ascending_array_name:ident $product_name:ident),+) => { + $( + #[wasm_bindgen] + pub fn $ascending_array_name() -> [i32; $N] { + let mut array = [0; $N]; + array.iter_mut().enumerate().for_each(|(idx, value)| *value = idx); + array + } + + #[wasm_bindgen] + pub fn $product_name(array: [i32; $N]) -> i32 { + from_js.iter().product() + } + )+ + } +} + +array_impls!( + 0 asceding_array0 product0, + 1 asceding_array1 product1, + 2 asceding_array2 product2, + 3 asceding_array3 product3, + 4 asceding_array4 product4, + 5 asceding_array5 product5, + 6 asceding_array6 product6, + 7 asceding_array7 product7, + 8 asceding_array8 product8, + 9 asceding_array9 product9, + 10 asceding_array10 product10, + 11 asceding_array11 product11, + 12 asceding_array12 product12, + 13 asceding_array13 product13, + 14 asceding_array14 product14, + 15 asceding_array15 product15, + 16 asceding_array16 product16, + 17 asceding_array17 product17, + 18 asceding_array18 product18, + 19 asceding_array19 product19, + 20 asceding_array20 product20, + 21 asceding_array21 product21, + 22 asceding_array22 product22, + 23 asceding_array23 product23, + 24 asceding_array24 product24, + 25 asceding_array25 product25, + 26 asceding_array26 product26, + 27 asceding_array27 product27, + 28 asceding_array28 product28, + 29 asceding_array29 product29, + 30 asceding_array30 product30, + 31 asceding_array31 product31, + 32 asceding_array32 product32, +); \ No newline at end of file