robertkrimen / otto

A JavaScript interpreter in Go (golang)
http://godoc.org/github.com/robertkrimen/otto
MIT License
8.01k stars 584 forks source link

`panic: strconv.ParseInt: parsing "toJSON": invalid syntax` on `ToValue` a `map` #488

Closed mertyildiran closed 1 year ago

mertyildiran commented 1 year ago

I have a data structure: map[int64]map[string]*Something

I'm trying to return that as a JavaScript object literal from a function:

o.Set("example", func(call otto.FunctionCall) otto.Value {
    // `myMap` is always `map[int64]map[string]*Something`

    vm := otto.New()
    value, err := vm.ToValue(myMap) // panics
    if err != nil {
        log.Println(err.Error())
        return otto.UndefinedValue()
    }

    return value
})

I get the error below when the example function is called from JavaScript:

panic: strconv.ParseInt: parsing "toJSON": invalid syntax [recovered]
    panic: strconv.ParseInt: parsing "toJSON": invalid syntax

goroutine 67 [running]:
github.com/robertkrimen/otto.catchPanic.func1()
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/error.go:252 +0x3a5
panic({0x1d7bcc0, 0xc0006f7a70})
    /usr/local/go/src/runtime/panic.go:1038 +0x215
github.com/robertkrimen/otto._goMapObject.toKey({{0x1d46860, 0xc000d766c0, 0x15}, {0x2526cf0, 0x1ca0be0}, {0x2526cf0, 0x1d48de0}}, {0x1ff03dd, 0x6})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/type_go_map.go:36 +0x8a
github.com/robertkrimen/otto.goMapGetOwnProperty(0xc0003e3860, {0x1ff03dd, 0x6})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/type_go_map.go:51 +0xd4
github.com/robertkrimen/otto.(*_object).getOwnProperty(...)
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/object.go:32
github.com/robertkrimen/otto.objectGetProperty(0xc0003e3860, {0x1ff03dd, 0x6})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/object_class.go:180 +0x30
github.com/robertkrimen/otto.(*_object).getProperty(...)
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/object.go:37
github.com/robertkrimen/otto.objectGet(0xc0003e3860, {0x1ff03dd, 0x0})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/object_class.go:192 +0x2c
github.com/robertkrimen/otto.(*_object).get(...)
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/object.go:42
github.com/robertkrimen/otto.builtinJSON_stringifyWalk({{0xc0007be180, 0x0, 0x0, {0x5, {0x1f73b80, 0xc000a42720}}, {0xc000a97cf8, 0x1, 0x1}, 0xc000975830}, ...}, ...)
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/builtin_json.go:200 +0x15e
github.com/robertkrimen/otto.builtinJSON_stringify({0xc0007be180, 0x0, 0x0, {0x5, {0x1f73b80, 0xc000a42720}}, {0xc000a97cf8, 0x1, 0x1}, 0xc000975830})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/builtin_json.go:179 +0x5c5
github.com/robertkrimen/otto.(*_object).call(0xc000a426c0, {0xc000c2a7f8, {0x1f73b80, 0xc000a42720}}, {0xc000a97cf8, 0xc000d765a0, 0x1}, 0x49, {0x0, {0x0, ...}, ...})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/type_function.go:200 +0x6d8
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeCallExpression(0xc0007be180, 0xc000a398f0, {0x0, 0x0, 0x0})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_expression.go:239 +0x3ff
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeExpression(0xc0007be180, {0x249c1a0, 0xc000a398f0})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_expression.go:43 +0x378
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeCallExpression(0xc0007be180, 0xc000a39890, {0x0, 0x0, 0x0})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_expression.go:188 +0x94c
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeExpression(0xc0007be180, {0x249c1a0, 0xc000a39890})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_expression.go:43 +0x378
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeStatement(0xc0007be180, {0x249c260, 0xc000975c90})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_statement.go:61 +0xbd8
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeStatementList(0xc000d764b0, {0xc000975c60, 0x1, 0xc000c2b210})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_statement.go:119 +0x95
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeStatement(0xc0007be180, {0x249c140, 0xc000a41530})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_statement.go:29 +0x4a5
github.com/robertkrimen/otto.(*_runtime).cmpl_call_nodeFunction(0xc0007be180, 0xc000a438c0, 0xc000d76450, 0xc000a32080, {0x466bce, {0x203000, 0x203000}}, {0xc000d72318, 0x1, 0x1})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate.go:60 +0x20b
github.com/robertkrimen/otto.(*_object).call(0xc000a438c0, {0x468f27, {0x1f73b80, 0xc000396a20}}, {0xc000d72318, 0x40, 0x1}, 0xe8, {0x0, {0x0, ...}, ...})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/type_function.go:225 +0x27c
github.com/robertkrimen/otto.(*_runtime).cmpl_evaluate_nodeCallExpression(0xc0007be180, 0xc000d763c0, {0xc000c2bde0, 0x1, 0x1})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/cmpl_evaluate_expression.go:239 +0x3ff
github.com/robertkrimen/otto.Otto.Call.func2()
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/otto.go:556 +0x36
github.com/robertkrimen/otto.catchPanic(0xc001316500)
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/error.go:255 +0x62
github.com/robertkrimen/otto.Otto.Call({0x0, 0xc0007be180}, {0x20012fb, 0xc}, {0x0, 0x0}, {0xc000c2bde0, 0x1, 0x1})
    /home/mertyildiran/go/pkg/mod/github.com/robertkrimen/otto@v0.2.1/otto.go:555 +0x456

Is int64 type supported as map key? If not then can we add it?

P.S. Couldn't use otto.ToValue because it says map is not supported here.

mertyildiran commented 1 year ago

Replacing int64 key type of the map with string as a workaround fixes the panic but defining a JavaScript object literal like {1: "foo", 2: "bar"} that integer as key is valid as far as I know. So, I believe ToValue function should support it.

stevenh commented 1 year ago

Thanks for reporting do you have a full test case for this?

mertyildiran commented 1 year ago

@stevenh this is as close as I can get while being minimal:

package main

import (
    "fmt"
    "log"

    "github.com/robertkrimen/otto"
)

type Something struct {
    Foo string `json:"foo"`
    Bar string `json:"bar"`
}

func main() {
    // Panicing map
    myMap := map[int64]map[string]*Something{
        0: {
            "a": &Something{
                Foo: "foo",
                Bar: "bar",
            },
            "b": &Something{
                Foo: "foo",
                Bar: "bar",
            },
        },
        1: {
            "a": &Something{
                Foo: "foo",
                Bar: "bar",
            },
            "b": &Something{
                Foo: "foo",
                Bar: "bar",
            },
        },
    }

    // This also panics
    // myMap := map[int64]string{
    //  0: "foo",
    //  1: "bar",
    // }

    // Working map
    // myMap := map[string]string{
    //  "a": "foo",
    //  "b": "bar",
    // }

    vm := otto.New()
    value, err := vm.ToValue(myMap) // panics
    if err != nil {
        log.Println(err.Error())
    } else {
        fmt.Printf("value: %+v\n", value)
    }
}

which returns a panic message for some reason: value: %!v(PANIC=String method: strconv.ParseInt: parsing "toString": invalid syntax)