wasmerio / wasmer-rust-example

Example of WebAssembly embedding in Rust using Wasmer
https://wasmerio.github.io/wasmer/rust/wasmer_runtime/
162 stars 24 forks source link

Using wasmer with context #4

Closed evgenykuzyakov closed 4 years ago

evgenykuzyakov commented 5 years ago

Let's say I want to call WASM code using some context. For example, I have a struct Boo and I want to use "self.a" as part of the WASM execution. Is it possible to do?

Here is the modified example:

extern crate wasmer_runtime;

use std::str;

use wasmer_runtime::{
    imports,
    instantiate,
    error,
    Ctx,
};

// Make sure that the compiled wasm-sample-app is accessible at this path.
static WASM: &'static [u8] = include_bytes!("../wasm-sample-app/target/wasm32-unknown-unknown/release/wasm_sample_app.wasm");

fn main() -> error::Result<()> {
    // Let's define the import object used to import our function
    // into our webassembly sample application.
    //
    // We've defined a macro that makes it super easy.
    //
    // The signature tells the runtime what the signature (the parameter
    // and return types) of the function we're defining here is.
    // The allowed types are `i32`, `u32`, `i64`, `u64`,
    // `f32`, and `f64`.
    //
    // Make sure to check this carefully!
    let boo = Boo{ a: 42 };

    boo.run()?;

    Ok(())
}

struct Boo {
    a: u32,
}

impl Boo {

    fn run(&self) -> error::Result<()> {
        let import_object = imports! {
            // Define the "env" namespace that was implicitly used
            // by our sample application.
            "env" => {
                // name         // func    // signature
                "print_str" => self.print_str<[u32, u32] -> []>,
            },
        };

        // Compile our webassembly into an `Instance`.
        let mut instance = instantiate(WASM, import_object)?;

        // Call our exported function!
        instance.call("hello_wasm", &[])?;

        Ok(())
    }

    // Let's define our "print_str" function.
    //
    // The declaration must start with "extern" or "extern "C"".
    extern fn print_str(&self, ptr: u32, len: u32, ctx: &mut Ctx) {
        // Get a slice that maps to the memory currently used by the webassembly
        // instance.
        //
        // Webassembly only supports a single memory for now,
        // but in the near future, it'll support multiple.
        //
        // Therefore, we don't assume you always just want to access first
        // memory and force you to specify the first memory.
        let memory = ctx.memory(0);

        // Get a subslice that corresponds to the memory used by the string.
        let str_slice = &memory[ptr as usize..(ptr + len) as usize];

        // Convert the subslice to a `&str`.
        let string = str::from_utf8(str_slice).unwrap();

        // Print it!
        println!("{} (and a = {})", string, self.a);
    }
}
evgenykuzyakov commented 5 years ago

So my current solution is the code below. Is this the best option right now?

extern crate wasmer_runtime;

use std::str;
use std::ffi::c_void;

use wasmer_runtime::{
    imports,
    instantiate,
    error,
    Ctx,
};

// Make sure that the compiled wasm-sample-app is accessible at this path.
static WASM: &'static [u8] = include_bytes!("../wasm-sample-app/target/wasm32-unknown-unknown/release/wasm_sample_app.wasm");

fn main() -> error::Result<()> {
    // Let's define the import object used to import our function
    // into our webassembly sample application.
    //
    // We've defined a macro that makes it super easy.
    //
    // The signature tells the runtime what the signature (the parameter
    // and return types) of the function we're defining here is.
    // The allowed types are `i32`, `u32`, `i64`, `u64`,
    // `f32`, and `f64`.
    //
    // Make sure to check this carefully!
    let import_object = imports! {
        // Define the "env" namespace that was implicitly used
        // by our sample application.
        "env" => {
            // name         // func    // signature
            "print_str" => print_str<[u32, u32] -> []>,
        },
    };

    let mut boo = Boo { a: 42 };

    // Compile our webassembly into an `Instance`.
    let mut instance = instantiate(WASM, import_object)?;

    instance.context_mut().data = &mut boo as *mut _ as *mut c_void;

    // Call our exported function!
    instance.call("hello_wasm", &[])?;

    Ok(())
}

struct Boo {
    a: u32
}

// Let's define our "print_str" function.
//
// The declaration must start with "extern" or "extern "C"".
extern fn print_str(ptr: u32, len: u32, ctx: &mut Ctx) {
    // Get a slice that maps to the memory currently used by the webassembly
    // instance.
    //
    // Webassembly only supports a single memory for now,
    // but in the near future, it'll support multiple.
    //
    // Therefore, we don't assume you always just want to access first
    // memory and force you to specify the first memory.
    let memory = ctx.memory(0);
    let boo: &mut Boo = unsafe { &mut *(ctx.data as *mut Boo) };

    // Get a subslice that corresponds to the memory used by the string.
    let str_slice = &memory[ptr as usize..(ptr + len) as usize];

    // Convert the subslice to a `&str`.
    let string = str::from_utf8(str_slice).unwrap();

    // Print it!
    println!("{} and a is {}", string, boo.a);
}
lachlansneff commented 5 years ago

Unfortunately, yes, that's the best way to go about this problem right now. We may be able to support importing rust closures into a wasm module in the future by use of our new Func type:

let import_object = imports! {
    "env" => {
        "print_str" => func!(|ptr: u32, len: u32, ctx| boo.print_str(ptr, len, ctx)),
        // or
        "print_str" => Func::new(|ptr: u32, len: u32, ctx| boo.print_str(ptr, len, ctx)),
    },
};

but this doesn't work currently.

Speaking-of, in the new version of wasmer-runtime that we're about to release, the syntax used by the imports! macro has changed slightly. Instead of requiring you to explicitly insert the function signature, it now deduces the function signature automatically. You can either of the syntaxes in the example I've written above (without it being a closure, just supply a normal, non-extern function there).

fn foo_bar(ctx: &mut Ctx) {}

let import_object = imports! {
    "env" => {
        "foo_bar" => func!(foo_bar),
    },
};
thedavidmeister commented 4 years ago

@lachlansneff do closures work yet?

torch2424 commented 4 years ago

cc @MarkMcCaskey As I am unsure if closures work? 🤔

MarkMcCaskey commented 4 years ago

@thedavidmeister Yes, we support closures now! I'll add it to my TODO list to update this example today and demonstrate them

MarkMcCaskey commented 4 years ago

Okay, it should be updated in this repo now! I'll update the docs on our website, too!

cyrillicw commented 3 years ago

Closures support only Fn and not FnMut?