'Correct way to provide minimal browser-api to Go wasm and returning lib-api back to client, without Global scope?
I'm trying to find a best/right way to implement minimal and safe client <-> wasm api while avoiding usage of window|global|self
context. I have found 3 working solutions so far which I wrote here as simplified self explaining example. I'm not sure what's the right solution and most likely there are besides these approaches 4th, 5th, nth
solutions. Has anybody found needle in a haystack solution?
// main.go
package main
import (
"syscall/js"
"unsafe"
)
// this would be needed to be here to satisfy this error?
// go:linkname only allowed in Go files that import "unsafe"
var _ unsafe.Pointer
//go:linkname jsGo syscall/js.jsGo
var jsGo js.Value
type ref uint32
type tmpValue struct {
_ [0]func()
ref ref
gcPtr *ref
}
func hello(this js.Value, args []js.Value) any {
println("hello " + this.Get("name").String())
return nil
}
func main() {
c := make(chan struct{}, 0)
jsapi1 := js.Global() // not a window anymore
client1 := jsapi1.Get("client")
println("replace Global:", client1.Get("name").String())
jsapi2 := jsGo.Get("jsapi")
client2 := jsapi2.Get("client")
println("jsGo.client:", client2.Get("name").String())
tmp := (*tmpValue)(unsafe.Pointer(&jsapi1))
tmp.ref = 7
jsapi3 := (*js.Value)(unsafe.Pointer(tmp))
client3 := jsapi3.Get("client")
println("hack the table:", client3.Get("name").String())
client3.Set("hello", js.FuncOf(hello))
jsapi3.Get("Object").Call("freeze", client3)
<-c
}
// go.js
export class Go {
async run (instance, clientClass = name => ({ name })) {
/* ... */
const createJsAPI = (client) => ({
client, // client api not in global wid
Object, // browser api's want to provide for wasm.
})
// 1 e.g. replace js.Global
const replaceGlobalThis = createJsAPI(clientClass('client 1'))
// 2 e.g. add minimal api to syscall/js.jsGo
this.jsapi = createJsAPI(clientClass('client 2'))
// 3 e.g. add new entry ref
const jsapi = createJsAPI(clientClass('client 3'))
this._values = [ NaN, 0, null, true, false, replaceGlobalThis, this, jsapi ]
this._ids = new Map([[0, 1], [null, 2], [true, 3], [false, 4], [replaceGlobalThis, 5], [this, 6], [jsapi, 7]])
/* ... */
}
}
// client could also be wrapped with Proxy
// new Proxy({}, { get: (_, key) => {} } )
export const Client = name => ({ name })
client
// index.js
import { Go, Client } from 'go.js'
const go = new Go()
const client = Client('client 3')
const wasm = await WebAssembly.instantiateStreaming(fetch(wasmUrl), go.importObject)
go.run(wasm.instance, client)
client.hello()
client.x = 1
OUTPUT:
replace Global: client 1
jsGo.client: client 2
hack the table: client 3
hello client 3
Error: TypeError: Cannot add property x, object is not extensible // as expected
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|