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

syscall/js: illegal invocation error on (js.Value).Call #26143

Closed
hajimehoshi opened this issue Jun 29, 2018 · 10 comments
Closed

syscall/js: illegal invocation error on (js.Value).Call #26143

hajimehoshi opened this issue Jun 29, 2018 · 10 comments

Comments

@hajimehoshi
Copy link
Member

hajimehoshi commented Jun 29, 2018

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

go version devel +8997ec1c4e Tue Jun 26 16:40:09 2018 +0000 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/hajimehoshi/Library/Caches/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/hajimehoshi/go"
GOPROXY=""
GORACE=""
GOROOT="/Users/hajimehoshi/go-code"
GOTMPDIR=""
GOTOOLDIR="/Users/hajimehoshi/go-code/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/7t/qw3np69559591s1v0mk5_p1m0000gn/T/go-build209465728=/tmp/go-build -gno-record-gcc-switches -fno-common"
VGOMODROOT=""

What did you do?

With misc/wasm/wasm_exec.js in the commit 8997ec1, prepare index.html

<!DOCTYPE html>
<canvas id="canvas">
<script src="wasm_exec.js"></script>
<script>
// Polyfill
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("test.wasm"), go.importObject).then(result => {
  go.run(result.instance);
});
</script>

and test.wasm from test.go

package main

import (
        "syscall/js"
)

func init() {
        js.Global().Get("WebGLRenderingContext").Get("prototype")
}

func main() {
        c := js.Global().Get("document").Call("getElementById", "canvas")
        gl := c.Call("getContext", "webgl", nil)
        gl.Call("getExtension", "WEBGL_lose_context")
        select{}
}

and see index.html via an HTTP server.

What did you expect to see?

No error.

What did you see instead?

panic: JavaScript error: Illegal invocation
wasm_exec.js:39 
wasm_exec.js:39 goroutine 1 [running]:
wasm_exec.js:39 syscall/js.Value.Call(0x7ff8000000000010, 0x18757, 0xc, 0xc024758, 0x1, 0x1, 0x7ff8000000000010)
wasm_exec.js:39 	/Users/hajimehoshi/go-code/src/syscall/js/js.go:191 +0x7
wasm_exec.js:39 main.main()
wasm_exec.js:39 	/Users/hajimehoshi/test/wasm/test.go:15 +0x5

Bisecting says that 8997ec1 is the culprit.

EDIT: Interestingly, the error doesn't happen (and deadlock error happens instead) without init function.

@hajimehoshi
Copy link
Member Author

CC @neelance

@bcmills bcmills added this to the Go1.11 milestone Jun 29, 2018
@bcmills
Copy link
Contributor

bcmills commented Jun 29, 2018

(CC @cherrymui)

@cherrymui
Copy link
Member

It looks to me the problem is that for each Javascript object that returns to Go as a js.Value, we associate the ref id to it, but if that ref id is copied or inherited to other object, getting value by ref id would get the wrong object. In this particular case, it first gets in prototype in init, which gets a ref id associate to it. The ref id then gets inherited by the context object.

@gopherbot
Copy link
Contributor

Change https://golang.org/cl/121835 mentions this issue: misc/wasm: make sure value ref id is unique

@hajimehoshi
Copy link
Member Author

Thank you taking care of this. The root cause was a JavaScript behavior that:

const m = new Map();
const a = Object.prototype;
m[a] = 'This is a'
const b = {};
console.log(m[b]);

shows 'This is a' in JavaScript? I didn't know that...

@neelance
Copy link
Member

@hajimehoshi The code you wrote above is equivalent to:

const m = new Map();
const a = Object.prototype;
m[String(a)] = 'This is a'
const b = {};
console.log(m[String(b)]);

and String(a) is "[object Object]", which is the same as String(b). The accessor operations for a JavaScript Map are get and set, not [] and []=. The latter is normal property access.

@hajimehoshi
Copy link
Member Author

Yes thank you, Map was not related to this issue.

@hajimehoshi
Copy link
Member Author

Rather, the root cause of this issue is

const _refProp = Symbol();
Object.prototype[_refProp] = 1;
console.log({}[_refProp]); // Shows 1

?

@neelance
Copy link
Member

Yes, properties on a prototype show on its instances. The CL above will fix this.

@hajimehoshi
Copy link
Member Author

Thank you for clarifying. JavaScript is hard...

@golang golang locked and limited conversation to collaborators Jul 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants