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

wasm2js example outdated and not working as expected #2428

Closed
nicolasmelo1 opened this issue Jan 20, 2021 · 5 comments
Closed

wasm2js example outdated and not working as expected #2428

nicolasmelo1 opened this issue Jan 20, 2021 · 5 comments
Labels

Comments

@nicolasmelo1
Copy link

nicolasmelo1 commented Jan 20, 2021

Summary

Hello, so i'm trying to run wasm in react native and since i cannot run webassembly code directly i thought about creating a wasm file, converting it to JS using wasm2js command and then using the generated file directly. Before testing in the react native ambient i'm making tests directly in the browser and for this case i'm using the wasm2js example provided in the wasm-bindgen examples folder. But following the EXACT steps defined by the example, the wasm2js is STILL fetching the original .wasm file and not using the .js file generated by wasm2js command

Expected Behavior

In reality, if i understood correctly the wasm2js.js should use thewasm2js_bg.js file and not the wasm2js_bg.wasm file. Since some browsers might not support WebAssembly.

What happens is that this command in build.sh

sed -i 's/wasm2js_bg.wasm/wasm2js_bg.js/' pkg/wasm2js.js

is doing nothing in the pkg/wasm2js.js file

Additional Details

So to make it absolutely clear here what i'm trying to do is:

1 - Create a rust package that should be used both by the browser and the mobile version of my app
2 - On the browser i'll use the .wasm directly with webpack
3 - On the mobile i'll convert the .wasm to JS so i just use JS code instead of .wasm code. (performance penalties are known but the core idea here is to be possible to use my rust functions already created without needing to rewrite it in javascript)
4 - On step 3 i'm not testing this already in the app, but instead i'm making tests directly in the browser, i want to run the example code using only the .js files and not fetching the '.wasm' as it is doing by default

STEP 4 is basically what i need to reproduce and facing issues with.

I've tried running the wasm2js generated .js file directly, the problem is that they import 'wbg' which is defined in the wasm2js.js file, so it's not working.

@alexcrichton
Copy link
Contributor

Hm sorry it's been quite some time since I looked at this example. It could be an issue with the example, but can you track down where the fetch of the wasm is happening? Perhaps you can work backwards from there to figure out where the issue is and what's not requesting the JS instead?

@nicolasmelo1
Copy link
Author

Hello, thanks from the response in advance.

So from what i saw

input = import.meta.url.replace(/\.js$/, '_bg.wasm');

on this line in init() function is the exact line the where it gets the route to the .wasm file and here

  • wasm2js.js:
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input);
}

is where it fetches for the .wasm file. i've tried changing this

  • wasm2js.js:
input = import.meta.url.replace(/\.js$/, '_bg.wasm');

to this

  • wasm2js.js:
input = import.meta.url.replace(/\.js$/, '_bg.js');

but still it didn't work as expected. Because it's still trying to create a WebAssembly intance in load, as you can see in load function in the js output it doesn't handle when there is no WebAssembly available in the browser

  • wasm2js.js:
async function load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {

        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports);

            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);

                } else {
                    throw e;
                }
            }
        }

        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);

    } else {

        const instance = await WebAssembly.instantiate(module, imports);

        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };

        } else {
            return instance;
        }
    }
}

on the '.js' file created by wasm2js it imports the wbg module, but since this module doesn't exist, i haven't found a way to make this file work with the wasm2js.js file outputed by the wasm-pack build

  • wasm2gs_bg.js:
import { __wbindgen_throw } from 'wbg';
// ... rest of code generated by wasm2js

@alexcrichton
Copy link
Contributor

Oh the wasm2js support was intended to be used with ES modules, I don't believe it works with --target web or no-modules since they use the WebAssembly object for instantiation which won't work.

@nicolasmelo1
Copy link
Author

nicolasmelo1 commented Jan 20, 2021

That's actually strange because on the example in the build.sh on the second line we compile targeting to --target web

But anyway, i've tried running

wasm-pack build

And it gives me two .js files:

  • wasm2js.js - which just exports wasm2js_bg.js (so it has only two lines)
  • wasm2js_bg.js - which doesn't initialize WebAssembly. (that's just what i needed)

Then i run the following command to create a new .js file from the wasm
wasm2js ./pkg/wasm2js_bg.wasm -o ./pkg/wasm2js_bg1.js

in the wasm2js_bg1.js file we end up with

import { __wbindgen_throw } from './wasm2js_bg.js';
// ... rest of code generated by wasm2js

and in wasm2js_bg.js which was generated by wasm-pack build i've changed:

import * as wasm from './wasm2js_bg.wasm';

to

import * as wasm from './wasm2js_bg1.js'; // file generated by wasm2js command

and now we end up with a circular import.

How i solved for my use case

For my use case i was able to prevent this from happening by removing this import in wasm2js_bg.js and using require('./wasm2js_bg1.js') whenever wasm was being called in wasm2js_bg.js

So i've changed this:

  • wasm2js_bg.js
export function greet() {
    var ret = wasm.greet();
    return Data.__wrap(ret);
}

to this

  • wasm2js_bg.js
export function greet() {
    var ret = require('./wasm2js_bg.js').greet();
    return Data.__wrap(ret);
}

But i don't think this is a good solution, especially if the idea is to target browsers which doesn't support require()

@mvniekerk
Copy link

I've got the same issue, also with same MO (Rust -> WASM -> JS -> React Native).
[email protected]:mvniekerk/brotli-rs2js.git

I'll be following @nicolasmelo1 's example and try get it running.

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

No branches or pull requests

3 participants