New value doesn't exist when pushed to slice within map
BrianLeishman opened this issue · comments
Brian Leishman commented
Go program example:
package main
import (
"fmt"
"github.com/dop251/goja"
_ "embed"
)
//go:embed handler.js
var js string
type Data struct {
Map map[string]any `json:"map"`
}
func main() {
vm := goja.New()
vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
_, err := vm.RunString(js)
if err != nil {
panic(err)
}
var handler func(data *Data) any
err = vm.ExportTo(vm.Get("handler"), &handler)
if err != nil {
panic(err)
}
data := &Data{
Map: map[string]any{"slice": []any{"foo"}},
}
handler(data)
fmt.Println(data)
}
handler.js
function handler(data) {
data.map.slice.push("bar");
}
As you can see in the js, I push a value onto the slice within the map. I would expect the output of this program to be
&{map[slice:[foo bar]]}
But instead, I get this
&{map[slice:[foo]]}
It works as expected if I rewrite my js to look like the following
function handler(data) {
const slice = data.map.slice;
slice.push("bar");
data.map.slice = slice;
}
Dmitry Panov commented
Yeah, I would expect that too. Unfortunately, this is not how Go works. Values in maps are not addressable, so when you take it out of a map you get a copy. Your code is equivalent to
m := make(map[string]any)
m["slice"] = []any{"foo"}
s := m["slice"].([]any)
s = append(s, "bar")
fmt.Println(m["slice"])
The solution is to always store non-primitive values as pointers in maps.
This behaviour is documented, see https://pkg.go.dev/github.com/dop251/goja#Runtime.ToValue