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

wasm: global functions from wasm not working from node #29845

Closed
mattetti opened this Issue Jan 20, 2019 · 18 comments

Comments

Projects
None yet
4 participants
@mattetti
Copy link
Contributor

mattetti commented Jan 20, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12beta2 Darwin/amd64

Does this issue reproduce with the latest release?

Yes

What did you do?

I am trying to call registered functions (aka callbacks) defined in the WebAssembly file from node js. I use the provided wasm_exec.js which does call into the main function without any issues. From what I could gather, we are missing a way to expose the registered functions in JS. But unfortunately I am not familiar enough with the implementation details nor did I find a lot of information online on the matter.

What did you expect to see?

I expected to be able to call my exposed functions from js outside of the browser.

What did you see instead?

A js exception

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 20, 2019

@neelance you might know if that's a user error, a missing feature or a bug. Any feedback or pointer to solve the challenge for would be greatly appreciated.

@agnivade

This comment has been minimized.

Copy link
Member

agnivade commented Jan 21, 2019

I believe this is something that you are trying to do ?

fn := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    // do your stuff here
})

js.Global().Set("myFunc", fn)

Now from JS, call myFunc().

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 21, 2019

@agnivade that's exact what I do, but myFunc isn't defined in the scope. At least not when try to call it after the wasm binary was instantiated. Some languages seem to out the functions in the instance.exports but I am a bit lost with what we are doing in Go and my node knowledge is limited. Thanks for looking into it and trying to help though, very much appreciated!

@agnivade

This comment has been minimized.

Copy link
Member

agnivade commented Jan 21, 2019

Ah yes, that just might be the case. In the browser, we have window as global. I am not sure how to define functions in the global scope for node from wasm.

@agnivade agnivade changed the title WebAssembly - can't called registered functions from node wasm: how to define global functions from wasm and call them from node Jan 21, 2019

@agnivade agnivade added the Question label Jan 21, 2019

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 21, 2019

In Node.js the global object is called global instead of window.

@agnivade

This comment has been minimized.

Copy link
Member

agnivade commented Jan 21, 2019

So then shouldn't js.Global().Set("myFunc", fn) work transparently for the browser and node both ?

@mikioh

This comment has been minimized.

Copy link
Contributor

mikioh commented Jan 21, 2019

@agnivade,

It might be better to have a look at https://github.com/tc39/proposal-global.

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 21, 2019

So then shouldn't js.Global().Set("myFunc", fn) work transparently for the browser and node both ?

Yes.

@agnivade

This comment has been minimized.

Copy link
Member

agnivade commented Jan 21, 2019

But that's not what's happening

but myFunc isn't defined in the scope.

That is why I thought setting an object in the global scope might not work for Node.js.

But if that's supposed to work, then I am not sure why myFunc doesn't get defined in the global scope. Or probably I have misunderstood the question somehow.

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 21, 2019

It should definitely be possible to call global.myFunc();. Someone please post some concrete code that is failing, otherwise we're just guessing about the issue.

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 21, 2019

I was traveling with very limited internet and could not upload a repro case. I did test calling global.myFunc() but it wasn't defined. I'm back home and will out together a quick example ASAP. Thanks everyone

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 22, 2019

@neelance here is the repro example: https://github.com/mattetti/go-node-wasm

The main Go file is here: https://github.com/mattetti/go-node-wasm/blob/master/src/wasm/main.go
JS code change: https://github.com/mattetti/go-node-wasm/blob/master/src/node/wasm_exec.js#L506

To run the code I use the following command: GOOS=js GOARCH=wasm go run -exec=./src/node/go_js_wasm_exec ./src/wasm/main.go

Error:

(node:40122) UnhandledPromiseRejectionWarning: TypeError: global.add is not a function
    at WebAssembly.instantiate.then (/Users/mattetti/Code/go-node-wasm/src/node/wasm_exec.js:506:28)

@agnivade agnivade changed the title wasm: how to define global functions from wasm and call them from node wasm: global functions from wasm not working from node Jan 22, 2019

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 22, 2019

The go.run(result.instance); is returning earlier than you expect. If you do this:

	fmt.Println("loading")
	fmt.Println("loading 2")
	js.Global().Set("add", js.FuncOf(add))

then you get:

loading
(node:85999) UnhandledPromiseRejectionWarning: TypeError: global.add is not a function
    at WebAssembly.instantiate.then (/Users/richard/Repos/go-node-wasm/src/node/wasm_exec.js:506:28)
loading 2

Instead, have your Go code call some JS global after it is done with setup.

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 22, 2019

@neelance thanks for looking into my issue. Moving the js global declaration into init() did indeed solve the declaration problem. Is that the expected behavior tho? I'm going to eventually update the wiki and I'm curious to know if that's a side effect of the implementation, that might be revisited or if that pattern is the definite medium term answer.

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 22, 2019

Using init does not make any difference. If you add fmt.Println before your call, you would still see the same issue.

In general you are not supposed to modify wasm_exec.js. This is internal code and if you edit it then there are no guarantees about the outcome. So this issue is invalid since it involves editing wasm_exec.js.

@neelance neelance closed this Jan 22, 2019

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 22, 2019

@neelance I still don't quite understand the issue and agree that it would be best to not have to modify wasm_exec.js, but then I'm still unclear on how to use go compiled wasm from node. Is there a way to do that without modifying wasm_exec.js, is that just not officially supported by the go implementation? I'm not trying to give you a hard time or complain, I'd just love to provide some guidance to anyone who might be interested in making their Go code available into a node app by using the WebAssembly compilation target.

@neelance

This comment has been minimized.

Copy link
Member

neelance commented Jan 22, 2019

Instead, have your Go code call some JS global after it is done with setup.

Essentially your JS code has to wait until your Go code is done with preparations.

Btw: Have you joined the #webassembly channel on the Gophers Slack? You can also get advice there.

@mattetti

This comment has been minimized.

Copy link
Contributor Author

mattetti commented Jan 23, 2019

Went there and got great feedback, what I was missing was how to let the js code know that the go code is ready. This comment is meant to help anyone finding this issue:

Agniva De Sarker shared the following solution:

So currently, your JS code starts after go.run(...). What I am saying is move all that code to a separate function, name it startCb or sth. And then after all your js.Global().Set() calls are done, call startCb using js.Global().Call("startCb")

In your main.go, do this -

func main() {
	js.Global().Set("add", js.FuncOf(add))
	js.Global().Call("startCb")
	fmt.Println("loading")
	wait()
}

And have this at the start of wasm_exec.js

global.startCb = () => {
  console.log(global.add(1, 1));
  console.log("...");
};
$GOOS=js GOARCH=wasm go1.12beta1 run -exec=./src/node/go_js_wasm_exec ./src/wasm/main.go 
2
...
loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment