Skip to content

Commit a985c2f

Browse files
committed
Add more webassembly tests
1 parent 4b233a2 commit a985c2f

File tree

3 files changed

+183
-5
lines changed

3 files changed

+183
-5
lines changed

lib/components_guide/rustler/math.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ defmodule ComponentsGuide.Rustler.Math do
1919

2020
def add(_, _), do: error()
2121
def reverse_string(_), do: error()
22-
def wasm_example(_, _), do: error()
22+
def wasm_example_0(_, _), do: error()
23+
def wasm_example_1_i32(_, _, _), do: error()
24+
def wasm_example_2_i32(_, _, _, _), do: error()
25+
26+
def wasm_example(source, f), do: wasm_example_0(source, f)
27+
def wasm_example(source, f, a), do: wasm_example_1_i32(source, f, a)
28+
def wasm_example(source, f, a, b), do: wasm_example_2_i32(source, f, a, b)
2329

2430
defp error, do: :erlang.nif_error(:nif_not_loaded)
2531
end

native/componentsguide_rustler_math/src/lib.rs

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ fn reverse_string(a: String) -> String {
1414
}
1515

1616
#[rustler::nif]
17-
fn wasm_example(source: String, f: String) -> Result<i32, Error> {
17+
fn wasm_example_0(source: String, f: String) -> Result<i32, Error> {
1818
// return Ok(5);
1919
//return Err(Error::Term(Box::new("hello")));
20-
return match wasm_example_interal(source, f) {
20+
return match wasm_example_0_internal(source, f) {
2121
Ok(v) => Ok(v),
2222
Err(e) => Err(Error::Term(Box::new(e.to_string())))
2323
}
2424
}
2525

26-
fn wasm_example_interal(source: String, f: String) -> Result<i32, anyhow::Error> {
26+
fn wasm_example_0_internal(source: String, f: String) -> Result<i32, anyhow::Error> {
2727
let engine = Engine::default();
2828

2929
// We start off by creating a `Module` which represents a compiled form
@@ -60,4 +60,100 @@ fn wasm_example_interal(source: String, f: String) -> Result<i32, anyhow::Error>
6060
return Ok(result);
6161
}
6262

63-
rustler::init!("Elixir.ComponentsGuide.Rustler.Math", [add, reverse_string, wasm_example]);
63+
#[rustler::nif]
64+
fn wasm_example_1_i32(source: String, f: String, a: i32) -> Result<i32, Error> {
65+
return match wasm_example_1_i32_internal(source, f, a) {
66+
Ok(v) => Ok(v),
67+
Err(e) => Err(Error::Term(Box::new(e.to_string())))
68+
}
69+
}
70+
71+
fn wasm_example_1_i32_internal(source: String, f: String, a: i32) -> Result<i32, anyhow::Error> {
72+
let engine = Engine::default();
73+
74+
// We start off by creating a `Module` which represents a compiled form
75+
// of our input wasm module. In this case it'll be JIT-compiled after
76+
// we parse the text format.
77+
let module = Module::new(&engine, source)?;
78+
79+
// A `Store` is what will own instances, functions, globals, etc. All wasm
80+
// items are stored within a `Store`, and it's what we'll always be using to
81+
// interact with the wasm world. Custom data can be stored in stores but for
82+
// now we just use `()`.
83+
let mut store = Store::new(&engine, ());
84+
85+
// With a compiled `Module` we can then instantiate it, creating
86+
// an `Instance` which we can actually poke at functions on.
87+
let instance = Instance::new(&mut store, &module, &[])?;
88+
89+
// The `Instance` gives us access to various exported functions and items,
90+
// which we access here to pull out our `answer` exported function and
91+
// run it.
92+
let answer = instance
93+
.get_func(&mut store, &f)
94+
.expect(&format!("{} was not an exported function", f));
95+
96+
// There's a few ways we can call the `answer` `Func` value. The easiest
97+
// is to statically assert its signature with `typed` (in this case
98+
// asserting it takes no arguments and returns one i32) and then call it.
99+
let answer = answer.typed::<(i32), i32, _>(&store)?;
100+
101+
// And finally we can call our function! Note that the error propagation
102+
// with `?` is done to handle the case where the wasm function traps.
103+
let result = answer.call(&mut store, (a))?;
104+
105+
return Ok(result);
106+
}
107+
108+
#[rustler::nif]
109+
fn wasm_example_2_i32(source: String, f: String, a: i32, b: i32) -> Result<i32, Error> {
110+
return match wasm_example_2_i32_internal(source, f, a, b) {
111+
Ok(v) => Ok(v),
112+
Err(e) => Err(Error::Term(Box::new(e.to_string())))
113+
}
114+
}
115+
116+
fn wasm_example_2_i32_internal(source: String, f: String, a: i32, b: i32) -> Result<i32, anyhow::Error> {
117+
let engine = Engine::default();
118+
119+
// We start off by creating a `Module` which represents a compiled form
120+
// of our input wasm module. In this case it'll be JIT-compiled after
121+
// we parse the text format.
122+
let module = Module::new(&engine, source)?;
123+
124+
// A `Store` is what will own instances, functions, globals, etc. All wasm
125+
// items are stored within a `Store`, and it's what we'll always be using to
126+
// interact with the wasm world. Custom data can be stored in stores but for
127+
// now we just use `()`.
128+
let mut store = Store::new(&engine, ());
129+
130+
// With a compiled `Module` we can then instantiate it, creating
131+
// an `Instance` which we can actually poke at functions on.
132+
let instance = Instance::new(&mut store, &module, &[])?;
133+
134+
// The `Instance` gives us access to various exported functions and items,
135+
// which we access here to pull out our `answer` exported function and
136+
// run it.
137+
let answer = instance
138+
.get_func(&mut store, &f)
139+
.expect(&format!("{} was not an exported function", f));
140+
141+
// There's a few ways we can call the `answer` `Func` value. The easiest
142+
// is to statically assert its signature with `typed` (in this case
143+
// asserting it takes no arguments and returns one i32) and then call it.
144+
let answer = answer.typed::<(i32, i32), i32, _>(&store)?;
145+
146+
// And finally we can call our function! Note that the error propagation
147+
// with `?` is done to handle the case where the wasm function traps.
148+
let result = answer.call(&mut store, (a, b))?;
149+
150+
return Ok(result);
151+
}
152+
153+
rustler::init!("Elixir.ComponentsGuide.Rustler.Math", [
154+
add,
155+
reverse_string,
156+
wasm_example_0,
157+
wasm_example_1_i32,
158+
wasm_example_2_i32
159+
]);

test/components_guide/rustler/math_test.exs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,80 @@ defmodule ComponentsGuide.Rustler.MathTest do
2222

2323
assert Math.wasm_example(wasm_source, "answer") == 42
2424
end
25+
26+
test "wasm_example/2 dsl" do
27+
# wasm_source = module(func(export("answer"), :result_i32, {:i32_const, 42}))
28+
# wasm_source = module(func(export("answer"), Result.i32, {I32.const, 42}))
29+
# wasm_source = module({:func, {:export, "answer"}, {:result, :i32}, {:i32_const, 42}})
30+
31+
wasm_source = """
32+
(module
33+
(func (export "answer") (result i32)
34+
i32.const 42
35+
)
36+
)
37+
"""
38+
39+
assert Math.wasm_example(wasm_source, "answer") == 42
40+
end
41+
42+
test "wasm_example/4 adding two numbers" do
43+
wasm_source = """
44+
(module
45+
(func $add (param $a i32) (param $b i32) (result i32)
46+
local.get $a
47+
local.get $b
48+
i32.add)
49+
(export "add" (func $add))
50+
)
51+
"""
52+
53+
assert Math.wasm_example(wasm_source, "add", 7, 5) == 12
54+
end
55+
56+
test "wasm_example/4 multiplying two numbers" do
57+
wasm_source = """
58+
(module
59+
(func $multiply (param $a i32) (param $b i32) (result i32)
60+
local.get $a
61+
local.get $b
62+
i32.mul)
63+
(export "multiply" (func $multiply))
64+
)
65+
"""
66+
67+
assert Math.wasm_example(wasm_source, "multiply", 7, 5) == 35
68+
end
69+
70+
test "wasm_example/4 checking a number is within a range" do
71+
wasm_source = """
72+
(module
73+
(func $validate (param $num i32) (result i32)
74+
(if (result i32)
75+
(i32.lt_u (local.get $num) (i32.const 1))
76+
(then return (i32.const 0))
77+
(else (if (result i32)
78+
(i32.gt_u (local.get $num) (i32.const 255))
79+
(then return (i32.const 0))
80+
(else return (i32.const 1))
81+
))
82+
)
83+
)
84+
(export "validate" (func $validate))
85+
)
86+
"""
87+
88+
assert Math.wasm_example(wasm_source, "validate", 0) == 0
89+
assert Math.wasm_example(wasm_source, "validate", 1) == 1
90+
assert Math.wasm_example(wasm_source, "validate", 10) == 1
91+
assert Math.wasm_example(wasm_source, "validate", 13) == 1
92+
assert Math.wasm_example(wasm_source, "validate", 255) == 1
93+
assert Math.wasm_example(wasm_source, "validate", 256) == 0
94+
end
95+
96+
# defwasm multiply(a, b) do
97+
# Wasm.func multiply(a, b) do
98+
# W32.mul(a, b)
99+
# end
100+
# end
25101
end

0 commit comments

Comments
 (0)