Skip to content

Implement *WasmAbi for array #2013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/cli-support/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub enum Descriptor {
U64,
F32,
F64,
Array,
Boolean,
Function(Box<Function>),
Closure(Box<Closure>),
Expand Down
14 changes: 11 additions & 3 deletions crates/cli-support/src/wit/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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 {
Expand Down
17 changes: 11 additions & 6 deletions crates/cli-support/src/wit/outgoing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down Expand Up @@ -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(
Expand Down
11 changes: 11 additions & 0 deletions examples/arrays/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
15 changes: 15 additions & 0 deletions examples/arrays/README.md
Original file line number Diff line number Diff line change
@@ -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!
7 changes: 7 additions & 0 deletions examples/arrays/index.js
Original file line number Diff line number Diff line change
@@ -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);
14 changes: 14 additions & 0 deletions examples/arrays/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
13 changes: 13 additions & 0 deletions examples/arrays/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
}
25 changes: 25 additions & 0 deletions examples/arrays/webpack.config.js
Original file line number Diff line number Diff line change
@@ -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'
};
50 changes: 50 additions & 0 deletions src/convert/slices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,53 @@ if_std! {
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
}
}


macro_rules! array_impls {
($($N:expr),+) => {
$(
unsafe impl<T> WasmAbi for [T; $N] {}

impl<T> FromWasmAbi for [T; $N]
where
T: Copy + FromWasmAbi<Abi = [T; $N]>
{
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<T> IntoWasmAbi for [T; $N]
where
T: Copy + IntoWasmAbi<Abi = [T; $N]>
{
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
);
19 changes: 19 additions & 0 deletions src/describe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tys! {
U64
F32
F64
ARRAY
BOOLEAN
FUNCTION
CLOSURE
Expand Down Expand Up @@ -184,3 +185,21 @@ impl<T: WasmDescribe> WasmDescribe for Clamped<T> {
T::describe();
}
}

macro_rules! array_impls {
($($N:expr),+) => {
$(
impl<T: WasmDescribe> 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
);
22 changes: 22 additions & 0 deletions tests/wasm/arrays.js
Original file line number Diff line number Diff line change
@@ -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);
}
};
72 changes: 72 additions & 0 deletions tests/wasm/arrays.rs
Original file line number Diff line number Diff line change
@@ -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,
);