Skip to content

Commit 9409e29

Browse files
committed
Get string results from wasm working
1 parent 6c5ef35 commit 9409e29

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

lib/components_guide/rustler/math.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ defmodule ComponentsGuide.Rustler.Math do
2323
def wasm_example_1_i32(_, _, _), do: error()
2424
def wasm_example_2_i32(_, _, _, _), do: error()
2525
def wasm_buffer_2_i32(_, _, _, _), do: error()
26+
def wasm_string_2_i32(_, _, _, _), do: error()
2627

2728
def wasm_example(source, f), do: wasm_example_0(source, f)
2829
def wasm_example(source, f, a), do: wasm_example_1_i32(source, f, a)
2930
def wasm_example(source, f, a, b), do: wasm_example_2_i32(source, f, a, b)
3031

3132
def wasm_buffer(source, f, a, b), do: wasm_buffer_2_i32(source, f, a, b)
33+
def wasm_string(source, f, a, b), do: wasm_string_2_i32(source, f, a, b)
3234

3335
defp error, do: :erlang.nif_error(:nif_not_loaded)
3436
end

native/componentsguide_rustler_math/src/lib.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use wasmtime::*;
22
//use anyhow::Error as anyhowError;
33
use rustler::{Atom, Env, Error, NifStruct, ResourceArc, Term};
4+
use std::convert::TryInto;
45

56
#[rustler::nif]
67
fn add(a: i64, b: i64) -> i64 {
@@ -121,6 +122,14 @@ fn wasm_buffer_2_i32(wat_source: String, f: String, a: i32, b: i32) -> Result<Ve
121122
}
122123
}
123124

125+
#[rustler::nif]
126+
fn wasm_string_2_i32(wat_source: String, f: String, a: i32, b: i32) -> Result<String, Error> {
127+
return match wasm_example_2_i32_string_internal(wat_source, f, a, b) {
128+
Ok(v) => Ok(v),
129+
Err(e) => Err(Error::Term(Box::new(e.to_string())))
130+
}
131+
}
132+
124133
fn wasm_example_2_i32_internal(wat_source: String, buffer: bool, f: String, a: i32, b: i32) -> Result<i32, anyhow::Error> {
125134
let engine = Engine::default();
126135

@@ -222,11 +231,62 @@ fn wasm_example_2_i32_n_internal(wat_source: String, buffer: bool, f: String, a:
222231
return Ok(result);
223232
}
224233

234+
fn wasm_example_2_i32_string_internal(wat_source: String, f: String, a: i32, b: i32) -> Result<String, anyhow::Error> {
235+
let engine = Engine::default();
236+
237+
// A `Store` is what will own instances, functions, globals, etc. All wasm
238+
// items are stored within a `Store`, and it's what we'll always be using to
239+
// interact with the wasm world. Custom data can be stored in stores but for
240+
// now we just use `()`.
241+
let mut store = Store::new(&engine, ());
242+
let mut linker = Linker::new(&engine);
243+
244+
let memory_ty = MemoryType::new(1, None);
245+
let memory = Memory::new(&mut store, memory_ty)?;
246+
linker.define(&store, "env", "buffer", memory)?;
247+
248+
// We start off by creating a `Module` which represents a compiled form
249+
// of our input wasm module. In this case it'll be JIT-compiled after
250+
// we parse the text format.
251+
let module = Module::new(&engine, wat_source)?;
252+
253+
// With a compiled `Module` we can then instantiate it, creating
254+
// an `Instance` which we can actually poke at functions on.
255+
// let instance = Instance::new(&mut store, &module, &[])?;
256+
let instance = linker.instantiate(&mut store, &module)?;
257+
258+
// The `Instance` gives us access to various exported functions and items,
259+
// which we access here to pull out our `answer` exported function and
260+
// run it.
261+
let answer = instance
262+
.get_func(&mut store, &f)
263+
.expect(&format!("{} was not an exported function", f));
264+
265+
// There's a few ways we can call the `answer` `Func` value. The easiest
266+
// is to statically assert its signature with `typed` (in this case
267+
// asserting it takes no arguments and returns one i32) and then call it.
268+
let answer = answer.typed::<(i32, i32), (i32, i32)>(&store)?;
269+
270+
// And finally we can call our function! Note that the error propagation
271+
// with `?` is done to handle the case where the wasm function traps.
272+
let (start, length) = answer.call(&mut store, (a, b))?;
273+
let start: usize = start.try_into().unwrap();
274+
let length: usize = length.try_into().unwrap();
275+
276+
let mut string_buffer: Vec<u8> = Vec::with_capacity(length);
277+
string_buffer.resize(length, 0);
278+
memory.read(&store, start, &mut string_buffer)?;
279+
let string = String::from_utf8(string_buffer)?;
280+
281+
return Ok(string);
282+
}
283+
225284
rustler::init!("Elixir.ComponentsGuide.Rustler.Math", [
226285
add,
227286
reverse_string,
228287
wasm_example_0,
229288
wasm_example_1_i32,
230289
wasm_example_2_i32,
231-
wasm_buffer_2_i32
290+
wasm_buffer_2_i32,
291+
wasm_string_2_i32
232292
]);

test/components_guide/rustler/math_test.exs

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

110110
assert Math.wasm_buffer(wasm_source, "main", 0, 0) == [256, 30]
111+
assert Math.wasm_string(wasm_source, "main", 0, 0) == "Know the length of this string"
111112
end
112113

113114
# defwasm multiply(a, b) do

0 commit comments

Comments
 (0)