Description
Summary
I've been wondering how to handle strings in python + wasm (with wasmer). There are proposals like reference types and interface types and low-level examples manipulating memory to handle strings. As wasmer implements at least reference types, is there an easy way to e.g. pass strings to exported wasm functions?
Additional details
I was trying this out with wasm-bindgen
and implemented the wasi example from wasmtime, but transformed the wasm module into a reactor module. This very easy function copies one given file (param1) to another location (param2).
use std::fs;
use std::io::{Read, Write};
fn process(input_fname: &str, output_fname: &str) -> Result<(), String> {
let mut input_file = fs::File::open(input_fname)
.map_err(|err| format!("error opening input {}: {}", input_fname, err))?;
let mut contents = Vec::new();
input_file
.read_to_end(&mut contents)
.map_err(|err| format!("read error: {}", err))?;
let mut output_file = fs::File::create(output_fname)
.map_err(|err| format!("error opening output {}: {}", output_fname, err))?;
output_file
.write_all(&contents)
.map_err(|err| format!("write error: {}", err))
}
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn load_model(input_fname: &str, output_fname: &str) -> () {
if let Err(err) = process(&input_fname, &output_fname) {
eprintln!("{}", err)
}
}
Compiling with cargo rustc --release --target wasm32-wasi -- -Z wasi-exec-model=reactor
results in a wasm module which includes wasm-bindgen imports and exports, including memory manipulation functions:
(import "__wbindgen_placeholder__" "__wbindgen_describe" (func $_ZN12wasm_bindgen19__wbindgen_describe17h1713c9eafed8f0bcE (type $t0)))
(import "__wbindgen_externref_xform__" "__wbindgen_externref_table_grow" (func $_ZN12wasm_bindgen9externref31__wbindgen_externref_table_grow17ha6f4f5bae0e7b807E (type $t6)))
(import "__wbindgen_externref_xform__" "__wbindgen_externref_table_set_null" (func $_ZN12wasm_bindgen9externref35__wbindgen_externref_table_set_null17h2ad33b4d577a8fa6E (type $t0)))
(import "wasi_snapshot_preview1" "fd_close" (func $_ZN4wasi13lib_generated22wasi_snapshot_preview18fd_close17h637d5166044e9087E (type $t6)))
(import "wasi_snapshot_preview1" "fd_read" (func $_ZN4wasi13lib_generated22wasi_snapshot_preview17fd_read17h4151fc4f09f71f43E (type $t7)))
(import "wasi_snapshot_preview1" "fd_write" (func $_ZN4wasi13lib_generated22wasi_snapshot_preview18fd_write17h7c5a0bb2c5af3798E (type $t7)))
(import "wasi_snapshot_preview1" "path_open" (func $_ZN4wasi13lib_generated22wasi_snapshot_preview19path_open17hc57443ec1b90b55dE (type $t8)))
(import "wasi_snapshot_preview1" "fd_prestat_get" (func $__wasi_fd_prestat_get (type $t3)))
(import "wasi_snapshot_preview1" "fd_prestat_dir_name" (func $__wasi_fd_prestat_dir_name (type $t5)))
(import "wasi_snapshot_preview1" "proc_exit" (func $__wasi_proc_exit (type $t0)))
(import "wasi_snapshot_preview1" "environ_sizes_get" (func $__wasi_environ_sizes_get (type $t3)))
(import "wasi_snapshot_preview1" "environ_get" (func $__wasi_environ_get (type $t3)))
...
(export "load_model" (func $load_model.command_export))
(export "__wbindgen_describe_load_model" (func $__wbindgen_describe_load_model.command_export))
(export "__externref_table_alloc" (func $__externref_table_alloc.command_export))
(export "__externref_table_dealloc" (func $__externref_table_dealloc.command_export))
(export "__externref_drop_slice" (func $__externref_drop_slice.command_export))
(export "__externref_heap_live_count" (func $__externref_heap_live_count.command_export))
(export "__wbindgen_exn_store" (func $__wbindgen_exn_store.command_export))
(export "__wbindgen_malloc" (func $__wbindgen_malloc.command_export))
(export "__wbindgen_realloc" (func $__wbindgen_realloc.command_export))
(export "__wbindgen_free" (func $__wbindgen_free.command_export))
So given this simple python program to call this function, how can I interact with it so that it passes parameters correctly? Note that this program works, when the exported functions has hard coded paths instead.
from wasmer import engine, wasi, Store, Module, ImportObject, Instance
from wasmer_compiler_cranelift import Compiler
import os
__dir__ = os.path.dirname(os.path.realpath(__file__))
wasm_bytes = open(__dir__ + '/smartcore_wasi_lib.wasm', 'rb').read()
store = Store(engine.JIT(Compiler))
module = Module(store, wasm_bytes)
wasi_version = wasi.get_version(module, strict=True)
wasi_env = \
wasi.StateBuilder('smartcore-wasi'). \
preopen_directory("."). \
finalize()
import_object = wasi_env.generate_import_object(store, wasi_version)
instance = Instance(module, import_object)
# works with no parameters, if paths are hard coded
# instance.exports.load_model()
instance.exports.load_model('test1.txt', 'test2.txt')