Skip to content

Commit fde4a6a

Browse files
authored
Add some missing error propagation (#179)
* Use MaybeLocal ToLocal instead of IsEmpty and ToLocalChecked (closes #177, merged in): Using a ToLocal makes it clearer that the error is being handled which makes uses of ToLocalChecked stand out more as places where we are missing error handling. * Add valueResult to get the value and error from C results (closes #178, merged in): * Remove dead code path in getError to handle non-errors * Rename getError to newJSError and move to errors.go * Add some missing error propagation: * Propogate errors in ObjectTemplate.NewInstance * Propogate new v8 string errors in Object.Get * Propagate NewPromiseResolver error * Propagate promise errors from C to Go * Remove unused Exception*Error C functions * Propagate v8::FunctionTemplate::GetFunction error from C to Go * Propagate new v8 string errors in Context.RunScript * Propagate NewValue errors for string and big.Int type values * Propagate v8::Value::ToObject error from C to Go * Propagate v8::Value::ToDetailString error from C to Go * Add a change log entry for adding missing error propagation
1 parent 9d9805c commit fde4a6a

12 files changed

+282
-243
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Removed error return value from NewObjectTemplate and NewFunctionTemplate. Panic if given a nil argument.
2323
- Function Call accepts receiver as first argument.
2424

25+
### Fixed
26+
- Add some missing error propagation
27+
2528
## [v0.6.0] - 2021-05-11
2629

2730
### Added

context.go

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (c *Context) RunScript(source string, origin string) (*Value, error) {
9494
rtn := C.RunScript(c.ptr, cSource, cOrigin)
9595
c.deregister()
9696

97-
return getValue(c, rtn), getError(rtn)
97+
return valueResult(c, rtn)
9898
}
9999

100100
// Global returns the global proxy object.
@@ -165,31 +165,16 @@ func goContext(ref int) C.ContextPtr {
165165
return ctx.ptr
166166
}
167167

168-
func getValue(ctx *Context, rtn C.RtnValue) *Value {
168+
func valueResult(ctx *Context, rtn C.RtnValue) (*Value, error) {
169169
if rtn.value == nil {
170-
return nil
170+
return nil, newJSError(rtn.error)
171171
}
172-
return &Value{rtn.value, ctx}
172+
return &Value{rtn.value, ctx}, nil
173173
}
174174

175-
func getObject(ctx *Context, rtn C.RtnValue) *Object {
175+
func objectResult(ctx *Context, rtn C.RtnValue) (*Object, error) {
176176
if rtn.value == nil {
177-
return nil
178-
}
179-
return &Object{&Value{rtn.value, ctx}}
180-
}
181-
182-
func getError(rtn C.RtnValue) error {
183-
if rtn.error.msg == nil {
184-
return nil
185-
}
186-
err := &JSError{
187-
Message: C.GoString(rtn.error.msg),
188-
Location: C.GoString(rtn.error.location),
189-
StackTrace: C.GoString(rtn.error.stack),
177+
return nil, newJSError(rtn.error)
190178
}
191-
C.free(unsafe.Pointer(rtn.error.msg))
192-
C.free(unsafe.Pointer(rtn.error.location))
193-
C.free(unsafe.Pointer(rtn.error.stack))
194-
return err
179+
return &Object{&Value{rtn.value, ctx}}, nil
195180
}

errors.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
package v8go
66

7+
// #include <stdlib.h>
8+
// #include "v8go.h"
9+
import "C"
710
import (
811
"fmt"
912
"io"
13+
"unsafe"
1014
)
1115

1216
// JSError is an error that is returned if there is are any
@@ -18,6 +22,18 @@ type JSError struct {
1822
StackTrace string
1923
}
2024

25+
func newJSError(rtnErr C.RtnError) error {
26+
err := &JSError{
27+
Message: C.GoString(rtnErr.msg),
28+
Location: C.GoString(rtnErr.location),
29+
StackTrace: C.GoString(rtnErr.stack),
30+
}
31+
C.free(unsafe.Pointer(rtnErr.msg))
32+
C.free(unsafe.Pointer(rtnErr.location))
33+
C.free(unsafe.Pointer(rtnErr.stack))
34+
return err
35+
}
36+
2137
func (e *JSError) Error() string {
2238
return e.Message
2339
}

function.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (fn *Function) Call(recv Valuer, args ...Valuer) (*Value, error) {
2828
fn.ctx.register()
2929
rtn := C.FunctionCall(fn.ptr, recv.value().ptr, C.int(len(args)), argptr)
3030
fn.ctx.deregister()
31-
return getValue(fn.ctx, rtn), getError(rtn)
31+
return valueResult(fn.ctx, rtn)
3232
}
3333

3434
// Invoke a constructor function to create an object instance.
@@ -44,7 +44,7 @@ func (fn *Function) NewInstance(args ...Valuer) (*Object, error) {
4444
fn.ctx.register()
4545
rtn := C.FunctionNewInstance(fn.ptr, C.int(len(args)), argptr)
4646
fn.ctx.deregister()
47-
return getObject(fn.ctx, rtn), getError(rtn)
47+
return objectResult(fn.ctx, rtn)
4848
}
4949

5050
// Return the source map url for a function.

function_template.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,12 @@ func NewFunctionTemplate(iso *Isolate, callback FunctionCallback) *FunctionTempl
6565

6666
// GetFunction returns an instance of this function template bound to the given context.
6767
func (tmpl *FunctionTemplate) GetFunction(ctx *Context) *Function {
68-
// TODO: Consider propagating the v8::FunctionTemplate::GetFunction error
69-
val_ptr := C.FunctionTemplateGetFunction(tmpl.ptr, ctx.ptr)
70-
return &Function{&Value{val_ptr, ctx}}
68+
rtn := C.FunctionTemplateGetFunction(tmpl.ptr, ctx.ptr)
69+
val, err := valueResult(ctx, rtn)
70+
if err != nil {
71+
panic(err) // TODO: Consider returning the error
72+
}
73+
return &Function{val}
7174
}
7275

7376
// Note that ideally `thisAndArgs` would be split into two separate arguments, but they were combined

json.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func JSONParse(ctx *Context, str string) (*Value, error) {
2222
defer C.free(unsafe.Pointer(cstr))
2323

2424
rtn := C.JSONParse(ctx.ptr, cstr)
25-
return getValue(ctx, rtn), getError(rtn)
25+
return valueResult(ctx, rtn)
2626
}
2727

2828
// JSONStringify tries to stringify the JSON-serializable object value and returns it as string.

object.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ func (o *Object) MethodCall(methodName string, args ...Valuer) (*Value, error) {
2424
defer C.free(unsafe.Pointer(ckey))
2525

2626
getRtn := C.ObjectGet(o.ptr, ckey)
27-
err := getError(getRtn)
27+
prop, err := valueResult(o.ctx, getRtn)
2828
if err != nil {
2929
return nil, err
3030
}
31-
fn, err := getValue(o.ctx, getRtn).AsFunction()
31+
fn, err := prop.AsFunction()
3232
if err != nil {
3333
return nil, err
3434
}
@@ -89,13 +89,13 @@ func (o *Object) Get(key string) (*Value, error) {
8989
defer C.free(unsafe.Pointer(ckey))
9090

9191
rtn := C.ObjectGet(o.ptr, ckey)
92-
return getValue(o.ctx, rtn), getError(rtn)
92+
return valueResult(o.ctx, rtn)
9393
}
9494

9595
// GetIdx tries to get a Value at a give Object index.
9696
func (o *Object) GetIdx(idx uint32) (*Value, error) {
9797
rtn := C.ObjectGetIdx(o.ptr, C.uint32_t(idx))
98-
return getValue(o.ctx, rtn), getError(rtn)
98+
return valueResult(o.ctx, rtn)
9999
}
100100

101101
// Has calls the abstract operation HasProperty(O, P) described in ECMA-262, 7.3.10.

object_template.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ func (o *ObjectTemplate) NewInstance(ctx *Context) (*Object, error) {
5555
return nil, errors.New("v8go: Context cannot be <nil>")
5656
}
5757

58-
// TODO: propagate v8 error
59-
valPtr := C.ObjectTemplateNewInstance(o.ptr, ctx.ptr)
60-
return &Object{&Value{valPtr, ctx}}, nil
58+
rtn := C.ObjectTemplateNewInstance(o.ptr, ctx.ptr)
59+
return objectResult(ctx, rtn)
6160
}
6261

6362
func (o *ObjectTemplate) apply(opts *contextOptions) {

promise.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ func NewPromiseResolver(ctx *Context) (*PromiseResolver, error) {
3939
if ctx == nil {
4040
return nil, errors.New("v8go: Context is required")
4141
}
42-
ptr := C.NewPromiseResolver(ctx.ptr)
43-
val := &Value{ptr, ctx}
44-
// TODO: Propagate Promise::Resolver::New error
45-
return &PromiseResolver{&Object{val}, nil}, nil
42+
rtn := C.NewPromiseResolver(ctx.ptr)
43+
obj, err := objectResult(ctx, rtn)
44+
if err != nil {
45+
return nil, err
46+
}
47+
return &PromiseResolver{obj, nil}, nil
4648
}
4749

4850
// GetPromise returns the associated Promise object for this resolver.
@@ -99,20 +101,24 @@ func (p *Promise) Then(cbs ...FunctionCallback) *Promise {
99101
p.ctx.register()
100102
defer p.ctx.deregister()
101103

102-
var ptr C.ValuePtr
104+
var rtn C.RtnValue
103105
switch len(cbs) {
104106
case 1:
105107
cbID := p.ctx.iso.registerCallback(cbs[0])
106-
ptr = C.PromiseThen(p.ptr, C.int(cbID))
108+
rtn = C.PromiseThen(p.ptr, C.int(cbID))
107109
case 2:
108110
cbID1 := p.ctx.iso.registerCallback(cbs[0])
109111
cbID2 := p.ctx.iso.registerCallback(cbs[1])
110-
ptr = C.PromiseThen2(p.ptr, C.int(cbID1), C.int(cbID2))
112+
rtn = C.PromiseThen2(p.ptr, C.int(cbID1), C.int(cbID2))
111113

112114
default:
113115
panic("1 or 2 callbacks required")
114116
}
115-
return &Promise{&Object{&Value{ptr, p.ctx}}}
117+
obj, err := objectResult(p.ctx, rtn)
118+
if err != nil {
119+
panic(err) // TODO: Return error
120+
}
121+
return &Promise{obj}
116122
}
117123

118124
// Catch invokes the given function if the promise is rejected.
@@ -121,6 +127,10 @@ func (p *Promise) Catch(cb FunctionCallback) *Promise {
121127
p.ctx.register()
122128
defer p.ctx.deregister()
123129
cbID := p.ctx.iso.registerCallback(cb)
124-
ptr := C.PromiseCatch(p.ptr, C.int(cbID))
125-
return &Promise{&Object{&Value{ptr, p.ctx}}}
130+
rtn := C.PromiseCatch(p.ptr, C.int(cbID))
131+
obj, err := objectResult(p.ctx, rtn)
132+
if err != nil {
133+
panic(err) // TODO: Return error
134+
}
135+
return &Promise{obj}
126136
}

0 commit comments

Comments
 (0)