servo / rust-mozjs

DEPRECATED - moved to servo/mozjs instead.
Mozilla Public License 2.0
293 stars 122 forks source link

Out of memory error when synchronously loading Wasm #454

Closed kubkon closed 5 years ago

kubkon commented 5 years ago

This might be related to #403 but I'd assume that the update has since come from the upstream? Also, I'm not sure whether this repo is the best place to report this potential issue, or perhaps servo/mozjs would have been a better fit.

Anyhow, assuming that #403 has successfully been addressed, while playing a bit with standalone SP and rust bindings, I've come across a rather unnerving issue with synchronous execution of Wasm. The Wasm binary was cross-compiled with rustc using wasm32-unknown-emscripten target with async calls purposely disabled. I've then tried parsing it in rust and injecting it into SP via a standard C callback mechanism like so (full code can be found here):

fn main() {
// ...
    unsafe {
       // ...
       let _readWasm_fn = JS_DefineFunction(
            ctx,
            global.into(),
            b"readWasm\0".as_ptr() as *const libc::c_char,
            Some(readWasm),
            0,
            0,
        );

        // init print funcs
        let javascript = String::from(
            "
            var Module = {'printErr': puts, 'print': puts};
            Module['wasmBinary'] = readWasm('main.wasm');
        ",
        ) + &wasm_js;

        rooted!(in(ctx) let mut rval = UndefinedValue());
        runtime
            .evaluate_script(global, &javascript, "nonane", 0, rval.handle_mut())
            .unwrap_or_else(|_| {
                report_pending_exception(ctx, true);
            });
    }
// ...
}

unsafe extern "C" fn readWasm(ctx: *mut JSContext, argc: u32, vp: *mut Value) -> bool {
    let args = CallArgs::from_vp(vp, argc);

    let arg = mozjs::rust::Handle::from_raw(args.get(0));
    let filename = mozjs::rust::ToString(ctx, arg);

    rooted!(in(ctx) let filename_root = filename);
    let filename = JS_EncodeStringToUTF8(ctx, filename_root.handle().into());
    let filename = CStr::from_ptr(filename);

    let contents = read_wasm(str::from_utf8(filename.to_bytes()).unwrap()).unwrap();

    rooted!(in(ctx) let mut rval = ptr::null_mut::<JSObject>());
    ArrayBuffer::create(ctx, CreateWith::Slice(&contents), rval.handle_mut()).unwrap();

    args.rval().set(ObjectValue(rval.get()));
    true
}

fn read_wasm(filename: &str) -> std::io::Result<Vec<u8>> {
    let mut file = File::open(filename)?;
    let mut contents = Vec::new();
    file.read_to_end(&mut contents)?;
    Ok(contents)
}

When the program is run, an out-of-memory error is thrown when the following line (in the glue JS code) is being executed:

// ...
    module = new WebAssembly.Module(getBinary()); // 'getBinary()' is a convenience fn that reads in bytes from "Module['wasmBinary']"
// ...

Now, I don't know whether that's an issue with the way I set up the SP using the provided rust-mozjs bindings, or whether the issue is more subtle than this. I do know that by now I've looked pretty much everywhere imaginable but alas failed to find an answer. So any help would be greatly appreciated! 😺

jdm commented 5 years ago

I strongly suspect you are missing this runtime configuration.

kubkon commented 5 years ago

@jdm Sweet, worked like a charm! Thanks a bunch! Man, I wish I posted a question here a tad bit sooner :wink: Anyhow, thanks again!

On a different yet somewhat related note, would you welcome any help with some more extensive docs? Say, guides, or examples in the actual rust-docs? If so, I'd be happy to spend some time on this and submit a couple of PRs.

jdm commented 5 years ago

We would be very grateful for documentation improvements!