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

How to expose a function to Javascript that takes a Uint32Array and returns a Uint32Array? #2402

Open
githubaccount256 opened this issue Dec 24, 2020 · 7 comments
Labels

Comments

@githubaccount256
Copy link

githubaccount256 commented Dec 24, 2020

Hello, sorry for the noob question but I am extremely new to WASM and this is the final step I need to get my code working, and I can't quite figure it out.

For my website I am looking to offload a very intensive computation to WASM, and I thought Rust would be a good fit for this. Basically, I would like to send a matrix from Javascript to my WASM library and have it process it and then return a new matrix.

I have already written all the Rust code that computes the matrix (in Rust I am using a Vec) and I have verified that it works. The only thing I need to do now is somehow figure out how to get Javascript to be able to send a Uint32Array to this function and then have it send back a Uint32Array back to Javascript.

Unfortunately, I am getting some errors. First I tried doing this:

#[wasm_bindgen]
pub fn calculate_matrix(pixels: &mut Vec<usize>) -> Vec<usize> {
  // elided
}

But that was giving me this compiler error:

error[E0277]: the trait bound `Vec<usize>: RefMutFromWasmAbi` is not satisfied
  --> src\lib.rs:23:1
   |
23 | #[wasm_bindgen]
   | ^^^^^^^^^^^^^^^ the trait `RefMutFromWasmAbi` is not implemented for `Vec<usize>`

I thought maybe it was because it was only supported for u32 and not usize and that it's not allowed to be a mutable reference, so I had my function take a Vec<u32> instead and just clone the vec in the function back to usize, and that kind of worked, except now I'm getting a runtime error.

When I run wasm-pack build I get around 1000 pages of compiled WASM text output, and I have figured out that it is due to me trying to return a Vec<u32 from the function. If I remove the return value then it compiles successfully. For some reason it is not letting me return a Vec<u32>, but I'm not sure why. I thought that it would allow this but Javascript would just interpret it as a Uint32Array.

I was looking through the runtime compile logs and found these snippets:

Fatal: error in validating input
Error: failed to execute `wasm-opt`: exited with exit code: 1
  full command: "C:\\Users\\Admin\\AppData\\Local\\.wasm-pack\\wasm-opt-171374efd61df962\\wasm-opt.exe" "C:\\Users\\Admin\\Desktop\\wasm-rust\\pkg\\wasm_rust_bg.wasm" "-o" "C:\\Users\\Admin\\Desktop\\wasm-rust\\pkg\\wasm_rust_bg.wasm-opt.wasm" "-O"
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.
@githubaccount256
Copy link
Author

Hmm, okay so I think what I might need to do is have the function take a &[u32] and return a Box<[u32]>, but that is still resulting in the same error in validating input runtime error. Even stranger, I found this page:

https://rustwasm.github.io/wasm-bindgen/reference/types/boxed-number-slices.html

And one of the code snippets gives me the same error:

#[wasm_bindgen]
pub fn return_boxed_number_slice() -> Box<[u32]> {
    (0..42).collect::<Vec<u32>>().into_boxed_slice()
}

Also results in me getting the same runtime error. Is it possible this used to be allowed but is now different and the docs haven't been updated? Or maybe I'm doing something else wrong?

@githubaccount256
Copy link
Author

Ah, it appears that was an unrelated bug (from here: rustwasm/wasm-pack#886 (comment)) and it has now compiled. Nice, progress!

@alexcrichton
Copy link
Contributor

You should be able to return Vec<u32> from a function as well as take &[u32] as well as an argument. Additionally you can take/return js_sys::Uint32Array which should work to. Have your issues been fixed at this point or are you still loooking for help?

@alpha027
Copy link

alpha027 commented Jul 23, 2023

I am a newbie too and I encountered this problem, I wrote a dummy function to test but it did not work:

// Rust
#[wasm_bindgen]
pub fn my_dummy_function(
        num_samples: u32,
    ) -> Vec<u32>
{
  let my_array: Vec<u32> = vec![12; num_samples as usize];

  //Clone::clone(&my_array)
  my_array
}

In the frontend the rust module is loaded like this:

import('../wasm-test-pack/pkg/myDummyPackage_bg.wasm').then((response) => {
      this.myFirstResult = response.add_one(1);
      console.log("Test dummy add: " + this.myFirstResult); // Works fine !! returns 2
      console.log("Test dummy vector: " + response.my_dummy_function(1,1)); // Only works if I take two arguments
    });

The strange thing is after compiling the rust-wasm pack, the myDummyPackage.d.ts has the following exports:
Note that the function add_one is correct and it's working while my_dummy_function takes two numbers when in rust it takes only one input.

export const memory: WebAssembly.Memory;
export function my_dummy_function(a: number, b: number): void;
export function add_one(a: number): number;
...

I am on ubuntu 22.04, rustc 1.71.0, I would really appreciate some help here :pray:  

@Liamolucko
Copy link
Collaborator

That's happening because you're importing the wrong file: you should be importing pkg/myDummyPackage.js, not pkg/myDummyPackage_bg.wasm.

pkg/myDummyPackage_bg.wasm is the underlying WebAssembly that gets created by rustc, and pkg/myDummyPackage.js is a JS wrapper for it created by wasm-bindgen. The reason that it needs to exist is that WebAssembly can only directly pass numbers to and from JS, which is what you're seeing in those TypeScript declarations; the JS wrapper then converts them to JS values (in this case, a Uint32Array).

@alpha027
Copy link

alpha027 commented Jul 24, 2023

Actually, when using the ".js" file I was not even able to get the result from the function add_one(a: number): number;, I also have not specified, but I use the angular framework. I use similar approach as this repo. When I use import and specify the ".js" file, this is what I get:
Screenshot from 2023-07-24 07-31-44
When I import using the "wasm" file:
Screenshot from 2023-07-24 07-30-41

I am actually not sure how much of the problem angular-related, also if there is another way to make the import using js that would fix the problem I would appreciate to know how :-)

@daxpedda
Copy link
Collaborator

The repo you linked instantiates the Wasm module by itself calling WebAssembly.instantiate(), if you gave it the JS file then the error message isn't surprising. The generated JS shim by wasm-bindgen exports a default function, which you should use to initialize the Wasm module.

Unfortunately I know nothing about Angular, but it would certainly help to see the code again you changed to get to where you are now.

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

5 participants