Skip to content

Commit 44253ba

Browse files
committed
Implement *WasmAbi for arrays
1 parent 93cb6cb commit 44253ba

File tree

13 files changed

+271
-9
lines changed

13 files changed

+271
-9
lines changed

crates/cli-support/src/descriptor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub enum Descriptor {
5353
U64,
5454
F32,
5555
F64,
56+
Array,
5657
Boolean,
5758
Function(Box<Function>),
5859
Closure(Box<Closure>),

crates/cli-support/src/wit/incoming.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,17 @@ impl InstructionBuilder<'_, '_> {
4545
fn _incoming(&mut self, arg: &Descriptor) -> Result<(), Error> {
4646
use walrus::ValType as WasmVT;
4747
use wit_walrus::ValType as WitVT;
48+
let vector_kind = |arg: &Descriptor| arg.vector_kind().ok_or_else(|| {
49+
format_err!("unsupported argument type for calling Rust function from JS {:?}", arg)
50+
});
4851
match arg {
52+
Descriptor::Array => {
53+
self.instruction(
54+
&[AdapterType::Vector(vector_kind(arg)?)],
55+
Instruction::I32FromAnyrefOwned,
56+
&[AdapterType::I32],
57+
);
58+
}
4959
Descriptor::Boolean => {
5060
self.instruction(
5161
&[AdapterType::Bool],
@@ -117,9 +127,7 @@ impl InstructionBuilder<'_, '_> {
117127
}
118128

119129
Descriptor::Vector(_) => {
120-
let kind = arg.vector_kind().ok_or_else(|| {
121-
format_err!("unsupported argument type for calling Rust function from JS {:?}", arg)
122-
})?;
130+
let kind = vector_kind(arg)?;
123131
self.instruction(
124132
&[AdapterType::Vector(kind)],
125133
Instruction::VectorToMemory {

crates/cli-support/src/wit/outgoing.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,17 @@ impl InstructionBuilder<'_, '_> {
2424
}
2525

2626
fn _outgoing(&mut self, arg: &Descriptor) -> Result<(), Error> {
27+
let vector_kind = |arg: &Descriptor| arg.vector_kind().ok_or_else(|| {
28+
format_err!("unsupported argument type for calling JS function from Rust {:?}", arg)
29+
});
2730
match arg {
31+
Descriptor::Array => {
32+
self.instruction(
33+
&[AdapterType::I32],
34+
Instruction::I32FromAnyrefOwned,
35+
&[AdapterType::Vector(vector_kind(arg)?)],
36+
);
37+
}
2838
Descriptor::Boolean => {
2939
self.instruction(
3040
&[AdapterType::I32],
@@ -129,12 +139,7 @@ impl InstructionBuilder<'_, '_> {
129139
}
130140

131141
Descriptor::Vector(_) => {
132-
let kind = arg.vector_kind().ok_or_else(|| {
133-
format_err!(
134-
"unsupported argument type for calling JS function from Rust {:?}",
135-
arg
136-
)
137-
})?;
142+
let kind = vector_kind(arg)?;
138143
let mem = self.cx.memory()?;
139144
let free = self.cx.free()?;
140145
self.instruction(

examples/arrays/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "arrays"
3+
version = "0.1.0"
4+
authors = ["The wasm-bindgen Developers"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["cdylib"]
9+
10+
[dependencies]
11+
wasm-bindgen = "0.2.58"

examples/arrays/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Working with arrays (up to 32)
2+
3+
[View documentation for this example online][dox] or [View compiled example
4+
online][compiled]
5+
6+
[compiled]: https://rustwasm.github.io/wasm-bindgen/exbuild/arrays/
7+
[dox]: https://rustwasm.github.io/docs/wasm-bindgen/examples/arrays.html
8+
9+
You can build the example locally with:
10+
11+
```
12+
$ npm run serve
13+
```
14+
15+
and then visiting http://localhost:8080 in a browser should run the example!

examples/arrays/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const rust = import('./pkg');
2+
rust
3+
.then(m => {
4+
console.log(m.asceding_array(10));
5+
console.log(m.product([1, 2, 3, 4]));
6+
})
7+
.catch(console.error);

examples/arrays/package.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"scripts": {
3+
"build": "webpack",
4+
"serve": "webpack-dev-server"
5+
},
6+
"devDependencies": {
7+
"@wasm-tool/wasm-pack-plugin": "1.0.1",
8+
"html-webpack-plugin": "^3.2.0",
9+
"text-encoding": "^0.7.0",
10+
"webpack-cli": "^3.1.1",
11+
"webpack-dev-server": "^3.1.0",
12+
"webpack": "^4.29.4"
13+
}
14+
}

examples/arrays/src/lib.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use wasm_bindgen::prelude::*;
2+
3+
#[wasm_bindgen]
4+
pub fn asceding_array(start: i32) -> [i32; 8] {
5+
let mut array = [0; 4];
6+
array.iter_mut().enumerate().for_each(|(idx, value)| *value = start + idx);
7+
array
8+
}
9+
10+
#[wasm_bindgen]
11+
pub fn product(from_js: [i32; 4]) -> i32 {
12+
from_js.iter().product()
13+
}

examples/arrays/webpack.config.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require('path');
2+
const HtmlWebpackPlugin = require('html-webpack-plugin');
3+
const webpack = require('webpack');
4+
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
5+
6+
module.exports = {
7+
entry: './index.js',
8+
output: {
9+
path: path.resolve(__dirname, 'dist'),
10+
filename: 'index.js',
11+
},
12+
plugins: [
13+
new HtmlWebpackPlugin(),
14+
new WasmPackPlugin({
15+
crateDirectory: path.resolve(__dirname, ".")
16+
}),
17+
// Have this example work in Edge which doesn't ship `TextEncoder` or
18+
// `TextDecoder` at this time.
19+
new webpack.ProvidePlugin({
20+
TextDecoder: ['text-encoding', 'TextDecoder'],
21+
TextEncoder: ['text-encoding', 'TextEncoder']
22+
})
23+
],
24+
mode: 'development'
25+
};

src/convert/slices.rs

+50
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,53 @@ if_std! {
271271
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
272272
}
273273
}
274+
275+
276+
macro_rules! array_impls {
277+
($($N:expr),+) => {
278+
$(
279+
unsafe impl<T> WasmAbi for [T; $N] {}
280+
281+
impl<T> FromWasmAbi for [T; $N]
282+
where
283+
T: Copy + FromWasmAbi<Abi = [T; $N]>
284+
{
285+
type Abi = [T; $N];
286+
287+
#[inline]
288+
unsafe fn from_abi(js: Self::Abi) -> Self {
289+
use core::convert::TryInto;
290+
let slice = slice::from_raw_parts(
291+
<*const T>::from_abi(js.as_ptr() as u32),
292+
js.len(),
293+
);
294+
slice.try_into().unwrap()
295+
}
296+
}
297+
298+
impl<T> IntoWasmAbi for [T; $N]
299+
where
300+
T: Copy + IntoWasmAbi<Abi = [T; $N]>
301+
{
302+
type Abi = [T; $N];
303+
304+
#[inline]
305+
fn into_abi(self) -> Self::Abi {
306+
unsafe {
307+
use core::convert::TryInto;
308+
let slice = slice::from_raw_parts(
309+
self.as_ptr(),
310+
self.len()
311+
);
312+
slice.try_into().unwrap()
313+
}
314+
}
315+
}
316+
)+
317+
}
318+
}
319+
320+
array_impls!(
321+
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,
322+
26, 27, 28, 29, 30, 31, 32
323+
);

src/describe.rs

+19
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tys! {
2727
U64
2828
F32
2929
F64
30+
ARRAY
3031
BOOLEAN
3132
FUNCTION
3233
CLOSURE
@@ -184,3 +185,21 @@ impl<T: WasmDescribe> WasmDescribe for Clamped<T> {
184185
T::describe();
185186
}
186187
}
188+
189+
macro_rules! array_impls {
190+
($($N:expr),+) => {
191+
$(
192+
impl<T: WasmDescribe> WasmDescribe for [T; $N] {
193+
fn describe() {
194+
inform(ARRAY);
195+
T::describe();
196+
}
197+
}
198+
)+
199+
}
200+
}
201+
202+
array_impls!(
203+
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,
204+
26, 27, 28, 29, 30, 31, 32
205+
);

tests/wasm/arrays.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const wasm = require('wasm-bindgen-test.js');
2+
const assert = require('assert');
3+
4+
exports.js_ascending_array = () => {
5+
for (let a = 0; a < 33; a ++) {
6+
assert.strictEqual(
7+
wasm["asceding_array" + a](),
8+
[...Array(a).keys()]
9+
);
10+
}
11+
};
12+
13+
exports.js_product = () => {
14+
let array = [];
15+
for (let a = 0; a < 33; a ++) {
16+
assert.strictEqual(
17+
wasm["product" + a](array),
18+
array.reduce((acc, value) => acc * value, 1)
19+
);
20+
array.push(a);
21+
}
22+
};

tests/wasm/arrays.rs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use wasm_bindgen::prelude::*;
2+
use wasm_bindgen_test::*;
3+
4+
#[wasm_bindgen(module = "tests/wasm/arrays.js")]
5+
extern "C" {
6+
fn js_ascending_array();
7+
fn js_product();
8+
}
9+
10+
#[wasm_bindgen_test]
11+
fn rust_ascending_array() {
12+
js_ascending_array();
13+
}
14+
15+
#[wasm_bindgen_test]
16+
fn rust_product() {
17+
js_product();
18+
}
19+
20+
macro_rules! array_impls {
21+
($($N:expr $ascending_array_name:ident $product_name:ident),+) => {
22+
$(
23+
#[wasm_bindgen]
24+
pub fn $ascending_array_name() -> [i32; $N] {
25+
let mut array = [0; $N];
26+
array.iter_mut().enumerate().for_each(|(idx, value)| *value = idx);
27+
array
28+
}
29+
30+
#[wasm_bindgen]
31+
pub fn $product_name(array: [i32; $N]) -> i32 {
32+
from_js.iter().product()
33+
}
34+
)+
35+
}
36+
}
37+
38+
array_impls!(
39+
0 asceding_array0 product0,
40+
1 asceding_array1 product1,
41+
2 asceding_array2 product2,
42+
3 asceding_array3 product3,
43+
4 asceding_array4 product4,
44+
5 asceding_array5 product5,
45+
6 asceding_array6 product6,
46+
7 asceding_array7 product7,
47+
8 asceding_array8 product8,
48+
9 asceding_array9 product9,
49+
10 asceding_array10 product10,
50+
11 asceding_array11 product11,
51+
12 asceding_array12 product12,
52+
13 asceding_array13 product13,
53+
14 asceding_array14 product14,
54+
15 asceding_array15 product15,
55+
16 asceding_array16 product16,
56+
17 asceding_array17 product17,
57+
18 asceding_array18 product18,
58+
19 asceding_array19 product19,
59+
20 asceding_array20 product20,
60+
21 asceding_array21 product21,
61+
22 asceding_array22 product22,
62+
23 asceding_array23 product23,
63+
24 asceding_array24 product24,
64+
25 asceding_array25 product25,
65+
26 asceding_array26 product26,
66+
27 asceding_array27 product27,
67+
28 asceding_array28 product28,
68+
29 asceding_array29 product29,
69+
30 asceding_array30 product30,
70+
31 asceding_array31 product31,
71+
32 asceding_array32 product32,
72+
);

0 commit comments

Comments
 (0)