2

I am trying to run a WebAssembly program (written in Rust, the example-program from https://rustwasm.github.io/book/game-of-life/hello-world.html) with Rocket. The WebAssembly is compiled with wasm-pack and using the wasm_bindgen. The wasm binary is represented as content::JavaScript<Vec<u8>> in Rocket and it seems like this is a 'working' solution. The binary is 'correctly' fetched, however Chrome prints a Uncaught SyntaxError: Invalid or unexpected token. Is this due to the representation as content::JavaScript<Vec<u8>> that there is an error during fetching (although the send bytes match the ones in the wasm file) or is the bug elsewhere?

I hope someone can explane me why there is a SyntaxError in a generated binary file.

Ben Müker
  • 31
  • 5

2 Answers2

1

Ok now i think i know what went wrong: The js file generated from the wasm-pack trys to load the WebAssembly as module. The module needts to have a Javascript mime otherwise it will fail (that is why i tried to send the wasm file as content::JavaScript<Vec<u8>>), but apparently loading a wasm as modul is not supported (correct me if i am wrong) so of course it will find an invalid token in a binary file because it trys to interpret it as plain javascript. What i actually use now is the Option<NamedFile> type from Rocket witch has a application/wasm mime.
I needed to change the generated js file a bit: the WebAssembly is initialized with WebAssembly.instatiateStreaming(fetch(...), importObjects), the module import should then be removed. The importObjects was a little tricky too, because passing strigns to WebAssembly is a little inconvenient. For an alert function that can be called from WebAssembly the importObjects looked like this:
let importObjects = {'./wasm_test': { __wbg_alert_3d9cbee15c16469e: __wbg_alert_3d9cbee15c16469e }};.
The names are from the wasm binary: (import "./wasm_test" "__wbg_alert_3d9cbee15c16469e" (func $__wbg_alert_3d9cbee15c16469e (type $t0)))
The function __wbg_alert_3d9cbee15c16469e is generated by the wasm-pack. The last thing to change is the object that got originally imported via a import statement. I have now a variable that has the content from obj.instance.exports which is set in the then statement from the WebAssembly.instatiateStreaming(fetch(...), importObjects).then(obj => { wasm = obj.instance.exports; })
With these changes it worked for me (sending and reading strings to/from WebAssembly)

Ben Müker
  • 31
  • 5
  • Do you have a link to a github repository of your complete code that works with Rocket and wasm and the example from the book? – Gardener Sep 04 '19 at 21:48
0

Following up on Ben's answer. The .wasm file is sent with an application/javascript mime thus the browser is trying to execute it as javascript. to send it with an application/wasm mime make sure your response is of type Option<rocket::response::NamedFile>.

Here's an example:

#[get("/pkg/<file..>")]
fn get_pkg(file : PathBuf) -> Option<rocket::response::NamedFile> {
   NamedFile::open(Path::new("client/pkg/").join(file)).ok()
}

Regarding the second issue - turns out rather than manually editing the javascript generated by wasm-pack build you can actually ask wasm-pack to generate javascript that can run in the browser and load your wasm. simply do wasm-pack build --target web.

All there is left to do is add something like this on your side:

<script type="module">
  import init from './pkg/client.js'; //client.js is the file generated by wasm
  async function run() {
    await init();
  }
  run();
</script>
tomer zeitune
  • 1,080
  • 1
  • 12
  • 14