Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using wasmer with context #4

Closed
evgenykuzyakov opened this issue Feb 6, 2019 · 7 comments · Fixed by #9
Closed

Using wasmer with context #4

evgenykuzyakov opened this issue Feb 6, 2019 · 7 comments · Fixed by #9

Comments

@evgenykuzyakov
Copy link

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
Copy link
Author

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
Copy link
Contributor

lachlansneff commented Feb 6, 2019

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
Copy link

@lachlansneff do closures work yet?

@torch2424
Copy link
Contributor

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

@MarkMcCaskey
Copy link
Contributor

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

@MarkMcCaskey
Copy link
Contributor

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

@cyrillicw
Copy link

Closures support only Fn and not FnMut?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants
@evgenykuzyakov @thedavidmeister @torch2424 @MarkMcCaskey @lachlansneff @cyrillicw and others