Skip to content

Commit 48eea5d

Browse files
committed
Allow passing list of args to wasm
1 parent e37a838 commit 48eea5d

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

lib/components_guide/rustler/math.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,25 @@ defmodule ComponentsGuide.Rustler.Math do
1919

2020
def add(_, _), do: error()
2121
def reverse_string(_), do: error()
22+
def wasm_example_n_i32(_, _, _), do: error()
2223
def wasm_example_0(_, _), do: error()
2324
def wasm_example_1_i32(_, _, _), do: error()
2425
def wasm_example_2_i32(_, _, _, _), do: error()
2526
def wasm_buffer_2_i32(_, _, _, _), do: error()
2627
def wasm_string_2_i32(_, _, _, _), do: error()
2728

2829
def wasm_example(source, f), do: wasm_example_0(source, f)
29-
def wasm_example(source, f, a), do: wasm_example_1_i32(source, f, a)
30-
def wasm_example(source, f, a, b), do: wasm_example_2_i32(source, f, a, b)
30+
def wasm_example(source, f, a), do: wasm_example_n_i32(source, f, [a]) |> process_result()
31+
def wasm_example(source, f, a, b), do: wasm_example_n_i32(source, f, [a, b]) |> process_result()
3132

32-
def wasm_buffer(source, f, a, b), do: wasm_buffer_2_i32(source, f, a, b) |> List.to_tuple()
33+
def wasm_buffer(source, f, a, b) do
34+
wasm_buffer_2_i32(source, f, a, b) |> process_result()
35+
end
3336
def wasm_string(source, f, a, b), do: wasm_string_2_i32(source, f, a, b)
3437

3538
defp error, do: :erlang.nif_error(:nif_not_loaded)
39+
40+
defp process_result([]), do: nil
41+
defp process_result([a]), do: a
42+
defp process_result(multiple_items), do: List.to_tuple(multiple_items)
3643
end

native/componentsguide_rustler_math/src/lib.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ fn reverse_string(a: String) -> String {
1414
return a.chars().rev().collect();
1515
}
1616

17+
#[rustler::nif]
18+
fn wasm_example_n_i32(source: String, f: String, args: Vec<i32>) -> Result<Vec<i32>, Error> {
19+
// return Ok(5);
20+
//return Err(Error::Term(Box::new("hello")));
21+
return match wasm_example_n_i32_internal(source, false, f, args) {
22+
Ok(v) => Ok(v),
23+
Err(e) => Err(Error::Term(Box::new(e.to_string())))
24+
}
25+
}
26+
1727
#[rustler::nif]
1828
fn wasm_example_0(source: String, f: String) -> Result<i32, Error> {
1929
// return Ok(5);
@@ -281,9 +291,68 @@ fn wasm_example_2_i32_string_internal(wat_source: String, f: String, a: i32, b:
281291
return Ok(string);
282292
}
283293

294+
fn wasm_example_n_i32_internal(wat_source: String, buffer: bool, f: String, args: Vec<i32>) -> Result<Vec<i32>, anyhow::Error> {
295+
let engine = Engine::default();
296+
297+
// A `Store` is what will own instances, functions, globals, etc. All wasm
298+
// items are stored within a `Store`, and it's what we'll always be using to
299+
// interact with the wasm world. Custom data can be stored in stores but for
300+
// now we just use `()`.
301+
let mut store = Store::new(&engine, ());
302+
let mut linker = Linker::new(&engine);
303+
304+
if buffer {
305+
let memory_ty = MemoryType::new(1, None);
306+
let memory = Memory::new(&mut store, memory_ty)?;
307+
linker.define(&store, "env", "buffer", memory)?;
308+
}
309+
310+
// We start off by creating a `Module` which represents a compiled form
311+
// of our input wasm module. In this case it'll be JIT-compiled after
312+
// we parse the text format.
313+
let module = Module::new(&engine, wat_source)?;
314+
315+
// With a compiled `Module` we can then instantiate it, creating
316+
// an `Instance` which we can actually poke at functions on.
317+
// let instance = Instance::new(&mut store, &module, &[])?;
318+
let instance = linker.instantiate(&mut store, &module)?;
319+
320+
// The `Instance` gives us access to various exported functions and items,
321+
// which we access here to pull out our `answer` exported function and
322+
// run it.
323+
let answer = instance
324+
.get_func(&mut store, &f)
325+
.expect(&format!("{} was not an exported function", f));
326+
327+
let func_type = answer.ty(&store);
328+
// There's a few ways we can call the `answer` `Func` value. The easiest
329+
// is to statically assert its signature with `typed` (in this case
330+
// asserting it takes no arguments and returns one i32) and then call it.
331+
// let answer = answer.typed::<(i32, i32), i32>(&store)?;
332+
333+
// let args = vec![a, b];
334+
// let args: &[Val] = &[Val::I32(a), Val::I32(b)];
335+
// let args: &[Val] = args.iter().map(|i| Val::I32(i)).collect();
336+
let args: Vec<Val> = args.into_iter().map(|i| Val::I32(i)).collect();
337+
338+
let mut result: Vec<Val> = Vec::with_capacity(16);
339+
// result.resize(2, Val::I32(0));
340+
let result_length = func_type.results().len();
341+
result.resize(result_length, Val::I32(0));
342+
343+
// And finally we can call our function! Note that the error propagation
344+
// with `?` is done to handle the case where the wasm function traps.
345+
answer.call(&mut store, &args, &mut result)?;
346+
347+
let result: Vec<_> = result.iter().map(|v| v.unwrap_i32()).collect();
348+
349+
return Ok(result);
350+
}
351+
284352
rustler::init!("Elixir.ComponentsGuide.Rustler.Math", [
285353
add,
286354
reverse_string,
355+
wasm_example_n_i32,
287356
wasm_example_0,
288357
wasm_example_1_i32,
289358
wasm_example_2_i32,

test/components_guide/rustler/math_test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ defmodule ComponentsGuide.Rustler.MathTest do
5151
"""
5252

5353
assert Math.wasm_example(wasm_source, "add", 7, 5) == 12
54+
assert Math.wasm_buffer(wasm_source, "add", 7, 5) == 12
5455
end
5556

5657
test "wasm_example/4 multiplying two numbers" do

0 commit comments

Comments
 (0)