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

No way to throw exceptions from native code #17

Open
BenLubar opened this issue May 17, 2013 · 11 comments
Open

No way to throw exceptions from native code #17

BenLubar opened this issue May 17, 2013 · 11 comments

Comments

@BenLubar
Copy link

In a func(otto.FunctionCall) otto.Value, there is no (documented) way to throw a JavaScript exception that the caller can see.

@robertkrimen
Copy link
Owner

Yes, this is something that needs work. Do you have any examples of what you're trying to achieve?

@cznic
Copy link

cznic commented May 19, 2013

FWIW, an example, schematically:

js helper

function mkfn(obj, fn) {
        var oldfn = obj[fn];
        obj[fn] = function() {
                var v = oldfn.apply(this, arguments);
                if (v.constructor == Error) {
                        throw v;
                };

                return v;
        };
}

Go errf

func (vm *VM) errf(e error) (r js.Value) {
        if e == nil {
                return js.UndefinedValue()
        }

        r, err := vm.jsvm.Run(fmt.Sprintf("new Error('%s')", template.JSEscapeString(e.Error())))
        if err != nil {
                log.Fatal(err)
        }

        return
}

Example native binding

func (vm *VM) dbArrays(o *js.Object, db *dbm.DB) {
        const (
                obj    = "db"
                method = "arrays"
        )
        if err := o.Set(method, func(call js.FunctionCall) (r js.Value) {
                defer func() {
                        if e := recover(); e != nil {
                                r = vm.errf(fmt.Errorf("%s.%s: %v", obj, method, e))
                        }
                }()

                a, err := db.Arrays()
                if err != nil {
                        panic(err)
                }

                o, err := vm.jsvm.Object(`new Object();`)
                if err != nil {
                        panic(err)
                }

                vm.array(o, &a)
                return o.Value()
        }); err != nil {
                panic(err)
        }

        if _, err := vm.mkfn.Call(js.UndefinedValue(), o, method); err != nil {
                panic(err)
        }
}

Full code: https://github.com/cznic/tmp/blob/14d211b7818be2225f12f80fa299f0176ae771cb/g/glue.go

@robertkrimen
Copy link
Owner

So what you're trying to do is throw a panic from the inner Go code to the other Go code?

@cznic
Copy link

cznic commented May 19, 2013

No, the machinery allows the javascript clients to catch errors reported by
Go native functions.
On May 19, 2013 6:56 PM, "Robert Krimen" [email protected] wrote:

So what you're trying to do is initiate a panic from the inner Go code to
the other Go code?


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-18120767
.

@robertkrimen
Copy link
Owner

Yes, I think a Go panic should be visible within a try { ... } catch { ... }, probably as a PanicError, with no extra work necessary.

I also think a (special) piercing panic should be possible, something that can bypass JavaScript.

@cznic
Copy link

cznic commented May 19, 2013

My example is not about Go panicking. It's about Go errors reporting. The
particular implementation reads data from a disk based database. Read
errors or format corruption may occur. The native funtion needs to throw a
javascript exception in that case - instead of returning, say invalid data
via the javasript wrapped native function - as that cannot support multiple
return values, which Go can.
On May 19, 2013 8:06 PM, "Robert Krimen" [email protected] wrote:

Yes, I think a Go panic should be visible within a try { ... } catch { ...
}, probably as a PanicError, with no extra work necessary.

I also think a (special) piercing panic should be possible, something that
can bypass JavaScript.


Reply to this email directly or view it on GitHubhttps://github.com//issues/17#issuecomment-18121942
.

@robertkrimen
Copy link
Owner

This is partially addressed in e2e79bb

If you panic() something of type otto.Value, then it will behave as if you did a "throw" in JavaScript.

For example: panic(Otto.ToValue("Hello, World.")) is the same as: throw "Hello, World.";

@robertkrimen
Copy link
Owner

This is further addressed in 83c56dd

I've added a .Call method to Otto, which allows you to call arbitrary JavaScript and return the result.

You can now do something like this in your Go code:

value, _ := call.Otto.Call("new Error", nil, "Something bad happened.")
panic(value)

If you want, a possible convenience function is:

func throw(value Value, _ error) Value {
     panic(value)
     return UndefinedValue()
}

With these two, you can do:

return throw(call.Otto.Call("new Error", nil, "Something bad happened.")

And it'll throw that exception in the JavaScript environment like you would expect

@cznic
Copy link

cznic commented May 20, 2013

Looks quite good to me. Thanks!

@robertkrimen
Copy link
Owner

Be careful when doing a raw panic of a complex object:

panic(someObject)
panic(valueOfAnObject)

The above may cause memory issues with the Go runtime, see #59

@BenLubar
Copy link
Author

If panic(someObject) uses memory even after recover() is called, that's a Go bug.

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

No branches or pull requests

4 participants