Skip to content

Commit

Permalink
Add Exception type and New*Error constructors.
Browse files Browse the repository at this point in the history
  • Loading branch information
tommie committed Oct 14, 2021
1 parent db84fcc commit 0c9a412
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 0 deletions.
109 changes: 109 additions & 0 deletions exception.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2021 Roger Chapman and the v8go contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package v8go

import (
// #include <stdlib.h>
// #include "v8go.h"
"C"

"fmt"
"unsafe"
)

// NewRangeError creates a RangeError.
func NewRangeError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_RANGE, msg)
}

// NewReferenceError creates a ReferenceError.
func NewReferenceError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_REFERENCE, msg)
}

// NewSyntaxError creates a SyntaxError.
func NewSyntaxError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_SYNTAX, msg)
}

// NewTypeError creates a TypeError.
func NewTypeError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_TYPE, msg)
}

// NewWasmCompileError creates a WasmCompileError.
func NewWasmCompileError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_WASM_COMPILE, msg)
}

// NewWasmLinkError creates a WasmLinkError.
func NewWasmLinkError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_WASM_LINK, msg)
}

// NewWasmRuntimeError creates a WasmRuntimeError.
func NewWasmRuntimeError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_WASM_RUNTIME, msg)
}

// NewError creates an Error, which is the common thing to throw from
// user code.
func NewError(iso *Isolate, msg string) *Exception {
return newExceptionError(iso, C.ERROR_GENERIC, msg)
}

func newExceptionError(iso *Isolate, typ C.ErrorTypeIndex, msg string) *Exception {
cmsg := C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
eptr := C.NewValueError(iso.ptr, typ, cmsg)
if eptr == nil {
panic(fmt.Errorf("invalid error type index: %d", typ))
}
return &Exception{&Value{ptr: eptr}}
}

// An Exception is a JavaScript exception.
type Exception struct {
*Value
}

// value implements Valuer.
func (e *Exception) value() *Value {
return e.Value
}

// Error implements error.
func (e *Exception) Error() string {
return e.String()
}

// As provides support for errors.As.
func (e *Exception) As(target interface{}) bool {
ep, ok := target.(**Exception)
if !ok {
return false
}
*ep = e
return true
}

// Is provides support for errors.Is.
func (e *Exception) Is(err error) bool {
eerr, ok := err.(*Exception)
if !ok {
return false
}
return eerr.String() == e.String()
}

// String implements fmt.Stringer.
func (e *Exception) String() string {
if e.Value == nil {
return "<nil>"
}
s := C.ExceptionGetMessageString(e.ptr)
defer C.free(unsafe.Pointer(s))
return C.GoString(s)
}
79 changes: 79 additions & 0 deletions exception_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2021 Roger Chapman and the v8go contributors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package v8go_test

import (
"errors"
"strings"
"testing"

v8 "rogchap.com/v8go"
)

func TestNewError(t *testing.T) {
t.Parallel()

tsts := []struct {
New func(*v8.Isolate, string) *v8.Exception
WantType string
}{
{v8.NewRangeError, "RangeError"},
{v8.NewReferenceError, "ReferenceError"},
{v8.NewSyntaxError, "SyntaxError"},
{v8.NewTypeError, "TypeError"},
{v8.NewWasmCompileError, "CompileError"},
{v8.NewWasmLinkError, "LinkError"},
{v8.NewWasmRuntimeError, "RuntimeError"},
{v8.NewError, "Error"},
}
for _, tst := range tsts {
t.Run(tst.WantType, func(t *testing.T) {
iso := v8.NewIsolate()
defer iso.Dispose()

got := tst.New(iso, "amessage")
if !got.IsNativeError() {
t.Error("IsNativeError returned false, want true")
}
if got := got.Error(); !strings.Contains(got, " "+tst.WantType+":") {
t.Errorf("Error(): got %q, want containing %q", got, tst.WantType)
}
})
}
}

func TestExceptionAs(t *testing.T) {
iso := v8.NewIsolate()
defer iso.Dispose()

want := v8.NewRangeError(iso, "faked error")

var got *v8.Exception
if !errors.As(want, &got) {
t.Fatalf("errors.As failed")
}

if got != want {
t.Errorf("errors.As: got %#v, want %#v", got, want)
}
}

func TestExceptionIs(t *testing.T) {
iso := v8.NewIsolate()
defer iso.Dispose()

t.Run("ok", func(t *testing.T) {
ex := v8.NewRangeError(iso, "faked error")
if !errors.Is(ex, v8.NewRangeError(iso, "faked error")) {
t.Fatalf("errors.Is: got false, want true")
}
})

t.Run("notok", func(t *testing.T) {
if errors.Is(errors.New("other error"), &v8.Exception{}) {
t.Fatalf("errors.Is: got true, want false")
}
})
}
53 changes: 53 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,48 @@ RtnValue NewValueBigIntFromWords(IsolatePtr iso,
return rtn;
}

ValuePtr NewValueError(IsolatePtr iso, ErrorTypeIndex idx, const char* message) {
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
Local<Context> local_ctx = ctx->ptr.Get(iso);
Context::Scope context_scope(local_ctx);

Local<String> local_msg = String::NewFromUtf8(iso, message).ToLocalChecked();
Local<Value> v;
switch (idx) {
case ERROR_RANGE:
v = Exception::RangeError(local_msg);
break;
case ERROR_REFERENCE:
v = Exception::ReferenceError(local_msg);
break;
case ERROR_SYNTAX:
v = Exception::SyntaxError(local_msg);
break;
case ERROR_TYPE:
v = Exception::TypeError(local_msg);
break;
case ERROR_WASM_COMPILE:
v = Exception::WasmCompileError(local_msg);
break;
case ERROR_WASM_LINK:
v = Exception::WasmLinkError(local_msg);
break;
case ERROR_WASM_RUNTIME:
v = Exception::WasmRuntimeError(local_msg);
break;
case ERROR_GENERIC:
v = Exception::Error(local_msg);
break;
default:
return nullptr;
}
m_value* val = new m_value;
val->iso = iso;
val->ctx = ctx;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, v);
return tracked_value(ctx, val);
}

const uint32_t* ValueToArrayIndex(ValuePtr ptr) {
LOCAL_VALUE(ptr);
Local<Uint32> array_index;
Expand Down Expand Up @@ -1030,6 +1072,17 @@ int ValueIsModuleNamespaceObject(ValuePtr ptr) {
return value->IsModuleNamespaceObject();
}

/********** Exception **********/

const char* ExceptionGetMessageString(ValuePtr ptr) {
LOCAL_VALUE(ptr);

Local<Message> local_msg = Exception::CreateMessage(iso, value);
Local<String> local_str = local_msg->Get();
String::Utf8Value utf8(iso, local_str);
return CopyString(utf8);
}

/********** Object **********/

#define LOCAL_OBJECT(ptr) \
Expand Down
14 changes: 14 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ typedef m_ctx* ContextPtr;
typedef m_value* ValuePtr;
typedef m_template* TemplatePtr;

typedef enum {
ERROR_RANGE = 1,
ERROR_REFERENCE,
ERROR_SYNTAX,
ERROR_TYPE,
ERROR_WASM_COMPILE,
ERROR_WASM_LINK,
ERROR_WASM_RUNTIME,
ERROR_GENERIC,
} ErrorTypeIndex;

typedef struct {
const char* msg;
const char* location;
Expand Down Expand Up @@ -115,6 +126,7 @@ extern RtnValue NewValueBigIntFromWords(IsolatePtr iso_ptr,
int sign_bit,
int word_count,
const uint64_t* words);
extern ValuePtr NewValueError(IsolatePtr iso_ptr, ErrorTypeIndex idx, const char* message);
const char* ValueToString(ValuePtr ptr);
const uint32_t* ValueToArrayIndex(ValuePtr ptr);
int ValueToBoolean(ValuePtr ptr);
Expand Down Expand Up @@ -181,6 +193,8 @@ int ValueIsProxy(ValuePtr ptr);
int ValueIsWasmModuleObject(ValuePtr ptr);
int ValueIsModuleNamespaceObject(ValuePtr ptr);

const char* ExceptionGetMessageString(ValuePtr ptr);

extern void ObjectSet(ValuePtr ptr, const char* key, ValuePtr val_ptr);
extern void ObjectSetIdx(ValuePtr ptr, uint32_t idx, ValuePtr val_ptr);
extern RtnValue ObjectGet(ValuePtr ptr, const char* key);
Expand Down
7 changes: 7 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,13 @@ func (v *Value) AsPromise() (*Promise, error) {
return &Promise{&Object{v}}, nil
}

func (v *Value) AsException() (*Exception, error) {
if !v.IsNativeError() {
return nil, errors.New("v8go: value is not an Error")
}
return &Exception{v}, nil
}

func (v *Value) AsFunction() (*Function, error) {
if !v.IsFunction() {
return nil, errors.New("v8go: value is not a Function")
Expand Down
21 changes: 21 additions & 0 deletions value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,27 @@ func TestValuePromise(t *testing.T) {

}

func TestValueAsException(t *testing.T) {
t.Parallel()

ctx := v8.NewContext()
defer ctx.Isolate().Dispose()
defer ctx.Close()

val, _ := ctx.RunScript("1", "")
if _, err := val.AsException(); err == nil {
t.Error("Expected error but got <nil>")
}
val, err := ctx.RunScript("new Error('foo')", "")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if _, err := val.AsException(); err != nil {
t.Errorf("Expected success but got: %v", err)
}

}

func TestValueFunction(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 0c9a412

Please sign in to comment.