#How to call JS function with 4 parameters? (wasm-bindgen)

1 messages · Page 1 of 1 (latest)

opal perch
#

i've found i can call javascript callbacks -- but js_sys::Function only comes with call0, call1, call2, and call3 -- i'm trying to figure out how i can add my own methods like call4 and such, so i can call functions with more params.
thanks for any help!

cosmic root
opal perch
#

i was told that i need to "define my own call4" -- but for the life of me, i can't figure out how to do that in rust

#

any way i try, i get the error
cannot define inherent 'impl' for a type outside of the crate where the type is defined

#

the way i was advised, this seems like this is straightforward in rust, i just can't figure out the right syntax to "define my own method" for this, to extend js_sys in this way

#

@cosmic root
i was looking in js_sys source code:

#[wasm_bindgen(method, catch, js_name = call)]
pub fn call3(
  this: &Function,
  context: &JsValue,
  arg1: &JsValue,
  arg2: &JsValue,
  arg3: &JsValue,
) -> Result<JsValue, JsValue>;

☝️ this is how call3 is provided -- i can't figure out how to simply add my own call4 definition by following the above pattern.
🤔 i could imagine forking js_sys and literally adding call4 through to call99 -- but surely that's ridiculous, surely this library is assuming that people familiar with rust know a simple way to "define their own" calling methods -- but for the life of me, i can't figure out the right pattern that doesn't cause the error i mentioned above?

cosmic root
#

have you tried just pasting that into your own code in an extern block and seeing what happens?

#

that seems like it might generate the right code

opal perch
#

yes, it gave me the above error -- i'll post some example of what i've tried

cosmic root
#

not in an impl

#

it's just a function

#

in order to make it an impl you would need to define an extension trait, which is a complication so don't bother with that for now

opal perch
#

i've tried this:

#[wasm_bindgen]
extern "C" {

    #[wasm_bindgen(method, catch, js_name = call)]
    pub fn call4(
            this: &js_sys::Function,
            context: &JsValue,
            arg1: &JsValue,
            arg2: &JsValue,
            arg3: &JsValue,
            arg4: &JsValue,
    ) -> Result<JsValue, JsValue>;
}
#

but i get error
error[E0116]: cannot define inherent 'impl' for a type outside of the crate where the type is defined

cosmic root
#

I don't know how you get a wasm-bindgen method to pass a JS this of your choice, though

#

(I am not at all an expert here; there may be a way)

opal perch
#

now, straight from the js_sys source code in lib.rs:

// Function
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(extends = Object, is_type_of = JsValue::is_function, typescript_type = "Function")]
    #[derive(Clone, Debug, PartialEq, Eq)]
    pub type Function;

    // ... more functions ...

    #[wasm_bindgen(method, catch, js_name = call)]
    pub fn call3(
        this: &Function,
        context: &JsValue,
        arg1: &JsValue,
        arg2: &JsValue,
        arg3: &JsValue,
    ) -> Result<JsValue, JsValue>;
}

☝️ seeing the context around call3 might help understand the situation enough to "define your own call4" ?

opal perch
#

hmm

cosmic root
#

right but that's the this for the function being called, that's not the function being called, which is the this of the JS method Function.prototype.call

opal perch
#

fascinating, that's getting slightly meta

cosmic root
#

that's the nature of .call!

opal perch
#

so, if i'm getting this right, #[wasm_bindgen] generates a function implementation.
i need to create call4.
but if i declare this on extern "C", i get the error that i can't mess around with implementations that belong to other crates.

cosmic root
#

that's not because of extern

#

that's because of #[wasm_bindgen(method)] _in particular

#

that's the problem

#

the macro is generating an impl Function {} block for you

#

which doesn't work if you didn't declare that type

#

this seems like a gap in wasm-bindgen's capabilities, to me, and I think whoever told you "define your own call4" either didn't know this, or was thinking you would write a JS function that forwards

#

you can still do that, if you want

#

write a little JS function that forwards 4 args, then write a wasm-bindgen declaration for that

opal perch
#

oh no, that sounds terrible.
so you're saying, it might be an undersight of wasm-bindgen's design -- it's not designed to be capable to pass more than 3 parameters?

#

is this something that wasm-bindgen needs a PR to fix?

#

i mean, it seems obvious to me that it's desirable to call functions and pass N arguments

cosmic root
opal perch
#

right, but that technique only allows me to import module-level functions -- that's not suitable for interacting with javascript at runtime

cosmic root
#

I mean they show

#
#[wasm_bindgen(inline_js = "export function add(a, b) { return a + b; }")]
extern "C" {
    fn add(a: u32, b: u32) -> u32;
}
#

so do this but with

export function call4(f, arg1, arg2, arg3, arg4) { return f(arg1, arg2, arg3, arg4); }
opal perch
#

okay, so the solution is to create my own js module, with functions for calling functions?

#

fascinating

cosmic root
#

that's what wasm-bindgen is doing anyway, in a sense; it has to generate lots of JS

#

this is just you providing some custom JS to go with the generated JS

opal perch
#

very interesting -- if i'm understanding you correctly, it does seem astonishing to me, that what feels like weird custom hacking, is required for basically any level of reasonable interop with javascript -- i just can hardly believe that this is what people are expected to do -- it makes me feel like i'm missing a much simpler straightforward way -- it hardly seems esoteric to simply want to call javascript functions in a straightforward manner... 🤔

#

@cosmic root i will now implement these helper functions and let you know how it goes

cosmic root
#

I think you're overestimating the average quality of libraries a bit!

opal perch
#

hahahah maybe so 😄

cosmic root
#

there are often things that are, in fact, just missing

#

and to get added, someone has to write the addition

#

and this doesn't happen until someone has a need for them

opal perch
#

amazing to think i'm one of the first to need to call javascript functions that might have more than 4 arguments 😄

cosmic root
#

notice that this generally only comes up with callbacks

#

lots of functions have 4 or more parameters but they belong to some namespace or class, and so they're called with generated wrappers for that function specifically, not via Function

opal perch
#

hmm fascinating

#

i started by trying to learn how to pass in the wasm importsObject, but apparently that's not possible -- i'm just trying to find a solid way for the rust code to call javascript functions -- but javascript functions at runtime because module-level functions are not useful to an ongoing javascript program...

#

that's how i've landed on making callbacks work, because wasm-bindgen doesn't give access to providing functions at runtime -- it's been quite a rabbit hole to say the least...

#

@cosmic root okay, here's what i've got:
➡️ src/callers.js

export function call4(func, context, arg1, arg2, arg3, arg4) {
    return func.call(context, arg1, arg2, arg3, arg4)
}

export function call5(func, context, arg1, arg2, arg3, arg4, arg5) {
    return func.call(context, arg1, arg2, arg3, arg4, arg5)
}

➡️ src/lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen(module = "/src/callers.js")]
extern "C" {
    fn call4(
        this: &js_sys::Function,
        context: &JsValue,
        arg1: &JsValue,
        arg2: &JsValue,
        arg3: &JsValue,
        arg4: &JsValue,
    ) -> Result<JsValue, JsValue>;
}

error[E0277]: the trait bound "Result<wasm_bindgen::JsValue, wasm_bindgen::JsValue>: FromWasmAbi" is not satisfied

#

huff...
i just want to declare in my rust code
"i expect these javascript functions to be provided at runtime"
and then be able to call those.
what you said earlier has me thinking, there surely must be a straightforward approach to do that.

cosmic root
#

I think you need the catch attr

#

that goes along with having the return type being -> Result

opal perch
#

but then suddenly the top of the extern block gives me this old error:

#[wasm_bindgen(module = "/src/callers.js")]
extern "C" {
    //...

error[E0116]: cannot define inherent "impl" for a type outside of the crate where the type is defined

cosmic root
#

you need catch but not method

opal perch
#

hey, no errors!

#

holy moly maybe this is it!

#

@cosmic root we did it 🎉 🎉 🎉
four parameters passed into javascript!

#
#[wasm_bindgen]
pub fn greet(name: &str, callback: &js_sys::Function) {
    let greeting = &format!("Hello, {}!", name);
    let this = JsValue::null();
    let greeting_js = JsValue::from(greeting);
    let argument2 = JsValue::from("argument2");
    let argument3 = JsValue::from("argument3");
    let argument4 = JsValue::from("argument4");
    let _ = call4(
        &callback,
        &this,
        &greeting_js,
        &argument2,
        &argument3,
        &argument4,
    );
}
#

Hello, Chase! argument2 argument3 argument4

#

it looks horrible, and hacky, and makes me a little sad -- but it works!

#

thanks so much @cosmic root, i hugely appreciate your help, it would have taken geological time for me to figure that out on my own

cosmic root
#

if the types are always those then you can remove some boilerplate by declaring them

#

that is, declare call4's signature to accept four &strs instead of JsValues

opal perch
#

right, interesting

cosmic root
#

of course that's making the function more single-purpose, not a generic function-caller

#

but that's an option you have because you're writing this function

opal perch
#

well, my goal is to chart out the "public javascript api" of functions that rust can call

cosmic root
#

and you get the static typing benefits

opal perch
#

from javascript, i want to

  1. "initialize the rust-wasm module"
  2. "provide rust with the following javascript functions, for rust to call"
  3. "call rust-defined functions"
#

it's been very painful reverse-engineering to build a very simple and straightforward bridge between rust's wasm output, and javascript.

#

i still have the sneaking suspicion that i'm misunderstanding a simpler way here, but i've read the documentation upside-down and sideways, and haven't figured a better path.
i've seen a lot of unanswered confusion, similar to mine, that it seems wasm-bindgen isn't really designed to interoperate with javascript as much as "replace" javascript -- wasm was designed to ingest a simple "imports" object containing javascript functions that become callable -- but wasm-bindgen offers no access to it 😦

#

once i learn rust more fully, it makes me feel compelled to perhaps one day fix this problem, and write a replacement library that handles this in a much simpler and straightforward way.

#

thanks again for your help.