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

Tinygo - target=wasm results in uncaught promise error in browser console. #4415

Open
owenwaller opened this issue Aug 16, 2024 · 3 comments

Comments

@owenwaller
Copy link

owenwaller commented Aug 16, 2024

Hi,

Can someone please clarify the level of support provided by tinygo (v0.32.0 - which is the latest AFAIK) with respect to a wasm target that is being executed in a browser, in comparison to Go v1.22.6.

I can currently seeing very different behaviour when I compile a trivial Go file to wasm using both compilers.

When I compile the Go code with tinygo I see the following error message in the browser console:

localhost/:1 Uncaught (in promise) 
TypeError: WebAssembly.instantiate(): Import #1 "wasi_snapshot_preview1": module is not an object or function
Promise.then (async)		
(anonymous)	@	(index):29

However if I compile the same code with Go v 1.226 and server it with the same web server (nginx in a container in my case) the wasm code works as I expect.

Is this a bug in tinygo? I am using the dockerised version of tinygo if that's any help.

Steps to reproduce

main.go

package main

import (
	"fmt"
)

func main() {
	fmt.Printf("Wasm hello\n")
}

index.html

<!doctype html>
<html>
<head>

<title>Wasm Test</title>

<meta charset="utf-8"/>


<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/encoding.min.js"></script> 
<script src="/wasm_exec.js"></script>
</head>
<body>

<img style="position: absolute; top: 50%; left: 50%;" src="https://cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif">

</div>
<script>
var wasmSupported = (typeof WebAssembly === "object");
if (wasmSupported) {
	if (!WebAssembly.instantiateStreaming) { 
		WebAssembly.instantiateStreaming = async (resp, importObject) => {
			const source = await (await resp).arrayBuffer();
			return await WebAssembly.instantiate(source, importObject);
		};
	}
	const go = new Go();
	WebAssembly.instantiateStreaming(fetch("/main.wasm"), go.importObject).then((result) => {
		go.run(result.instance);
	});
} else {
	document.getElementById("vugu_mount_point").innerHTML = 'This application requires WebAssembly support.  Please upgrade your browser.';
}
</script>
</body>
</html>

The go.mod is:

module example.com/wasm_http_example

go 1.22.6

The wasm_exec.js is taken from go env GOROOT/misc/wasm/wasm_exec.js`. This is my Go v1.22.6 root.

The tinygo build cmd is:

docker run --rm -v $(pwd):/home/tinygo tinygo/tinygo:0.32.0 tinygo build -o main.wasm -target=wasm main.go

executed in the the directory where the source code is, so the volume map works.

The standard Go build cmd is:

 GOOS=js GOARCH=wasm go build -o main.wasm main.go

Again from the directory that contains the source code.

The resulting main.wasm is then served using a standard nginx container like this:

 docker run --name wasm-nginx --mount type=bind,source=`pwd`,target=/usr/share/nginx/html,readonly -p 8888:80 -d nginx

Again from the directory that contains the source code.

If I serve the main.wasm build with the standard Go v1.22.6 compiler I see Wasm hello in the browser console. If I serve the version built with tinygo I see the uncaught promise error.

This is 100% reproducible, as far as I can tell.

Does anyone have any idea of the cause and is there a fix?

Many thanks

Owen

Updated to add: I see the same behaviour is I use Go v 1.23 in comparison to tinggo v0.32.0.
So with go 1.23 nothing works. I had a old (read go 1.22.6) module cache locally and an outdated `go.mod that specified go v1.22.6.

If I update the go.mod to:

module example.com/wasm_http_example

go 1.23

I then see this when I attempt to build:

$docker  run --rm -v $(pwd):/home/tinygo tinygo/tinygo:latest tinygo build -o main.wasm -target=wasm main.go
error: requires go version 1.19 through 1.22, got go1.23
owen@carbonx1:/tmp/wasm-http-example$ docker  run --rm -v $(pwd):/go tinygo/tinygo-dev:latest tinygo build -o main.wasm -target=wasm main.go
go: warning: ignoring go.mod in $GOPATH /go
go: go.mod requires go >= 1.23 (running go 1.22.6; GOTOOLCHAIN=local)

$ go version
go version go1.23.0 linux/amd64

$ ls go/pkg/mod/cache/download/golang.org/toolchain/@v/v0.0.1-go1.23.0.linux-amd64.* # from the local `go` directory
go/pkg/mod/cache/download/golang.org/toolchain/@v/v0.0.1-go1.23.0.linux-amd64.lock
go/pkg/mod/cache/download/golang.org/toolchain/@v/v0.0.1-go1.23.0.linux-amd64.zip
go/pkg/mod/cache/download/golang.org/toolchain/@v/v0.0.1-go1.23.0.linux-amd64.ziphash

So that looks like Go v1.23 support isn't in place yet.

Updated to add: if I use the bleeding edge docket image tinygo/tinygo-dev@latest I still see the same uncaught exception error in the browser window. Again against Go v1.23. But in addition I now see this on the command like:

docker run --rm -v $(pwd):/go tinygo/tinygo-dev:latest tinygo build -o main.wasm -target=wasm main.go
go: warning: ignoring go.mod in $GOPATH /go

The warning is new, and as you can see from the docker command the volume mapping now requires mapping into /go on the container (it was /home/tinygo).

@deadprogram
Copy link
Member

Make sure you copy wasm_exec.js to your runtime environment.

Via https://tinygo.org/docs/guides/webassembly/wasm/

Also note that the TinyGo wasm_exec.js file is different from the "big Go" file.

Hope that helps!

@owenwaller
Copy link
Author

@deadprogram

Ah ha, that is indeed the problem. If I swap the wasm_exec.js for the one currently on the master branch:

https://github.com/tinygo-org/tinygo/blob/release/targets/wasm_exec.js

Then all is good using with tinygo v 0.32.0. Well spotted, thank you.

Is there any thing the tinygo team can do to spit this sort of mistake at runtime? Or at least make the error message meaningful?

@owenwaller
Copy link
Author

Updated to add:

A similar situation exists with the standard Go compiler i.e. the version of wasm_exec.js must match the version of the compiler.

But, given a wasm_exec.js e.g. one that is committed to a repository, it is impossible to tell if that version of wasm_exec.js is compatible with the local Go toolchain.

I have raised this on the golang-nuts mailing list, along with a proposed solution - to embed a version string as a comment into wasm_exec.js See:

https://groups.google.com/g/golang-nuts/c/XJuEayu2KmE

Could the tinygo team please comment on this, and also consider a similar solution, or follow whatever solution to Go project uses?

Thanks

Owen

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

No branches or pull requests

2 participants