Skip to content

Commit

Permalink
Add the ThrowException fn to the isolate (rogchap#221)
Browse files Browse the repository at this point in the history
* Add the ThrowException fn to the isolate

* Add test for ThrowException

* Add comments, changelog, & simplify test

* Update isolate.go

* Pass isolate

* Panic if ThrowException is called on a disposed isolate

* Add test for disposed isolate check

Co-authored-by: Genevieve <genlesperance@gmail.com>
Co-authored-by: Genevieve <genevieve.lesperance@shopify.com>
Co-authored-by: Dylan Thacker-Smith <Dylan.Smith@shopify.com>
  • Loading branch information
4 people authored Nov 10, 2021
1 parent 4a6bcfb commit d8d94c2
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for setting and getting internal fields for template object instances
- Support for CPU profiling
- Add V8 build for Apple Silicon
- Add support for throwing an exception directly via the isolate's ThrowException function.
- Support for compiling a context-dependent UnboundScript which can be run in any context of the isolate it was compiled in.
- Support for creating a code cache from an UnboundScript which can be used to create an UnboundScript in other isolates
to run a pre-compiled script in new contexts.
Expand Down
13 changes: 13 additions & 0 deletions isolate.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ func (i *Isolate) Dispose() {
i.ptr = nil
}

// ThrowException schedules an exception to be thrown when returning to
// JavaScript. When an exception has been scheduled it is illegal to invoke
// any JavaScript operation; the caller must return immediately and only after
// the exception has been handled does it become legal to invoke JavaScript operations.
func (i *Isolate) ThrowException(value *Value) *Value {
if i.ptr == nil {
panic("Isolate has been disposed")
}
return &Value{
ptr: C.IsolateThrowException(i.ptr, value.ptr),
}
}

// Deprecated: use `iso.Dispose()`.
func (i *Isolate) Close() {
i.Dispose()
Expand Down
56 changes: 56 additions & 0 deletions isolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,62 @@ func TestIsolateGarbageCollection(t *testing.T) {
time.Sleep(time.Second)
}

func TestIsolateThrowException(t *testing.T) {
t.Parallel()
iso := v8.NewIsolate()

strErr, _ := v8.NewValue(iso, "some type error")

throwError := func(val *v8.Value) {
v := iso.ThrowException(val)

if !v.IsNullOrUndefined() {
t.Error("expected result to be null or undefined")
}
}

// Function that throws a simple string error from within the function. It is meant
// to emulate when an error is returned within Go.
fn := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
throwError(strErr)

return nil
})

// Function that is passed a TypeError from JavaScript.
fn2 := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
typeErr := info.Args()[0]

throwError(typeErr)

return nil
})

global := v8.NewObjectTemplate(iso)
global.Set("foo", fn)
global.Set("foo2", fn2)

ctx := v8.NewContext(iso, global)

_, e := ctx.RunScript("foo()", "foo.js")

if e.Error() != "some type error" {
t.Errorf("expected \"some type error\" error but got: %v", e)
}

_, e = ctx.RunScript("foo2(new TypeError('this is a test'))", "foo.js")

if e.Error() != "TypeError: this is a test" {
t.Errorf("expected \"TypeError: this is a test\" error but got: %v", e)
}

ctx.Close()
iso.Dispose()
if recoverPanic(func() { iso.ThrowException(strErr) }) == nil {
t.Error("expected panic")
}
}

func BenchmarkIsolateInitialization(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
Expand Down
17 changes: 17 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,23 @@ RtnUnboundScript IsolateCompileUnboundScript(IsolatePtr iso, const char* s, cons
return rtn;
}

/********** Exceptions & Errors **********/

ValuePtr IsolateThrowException(IsolatePtr iso, ValuePtr value) {
ISOLATE_SCOPE(iso);
m_ctx* ctx = value->ctx;

Local<Value> throw_ret_val = iso->ThrowException(value->ptr.Get(iso));

m_value* new_val = new m_value;
new_val->iso = iso;
new_val->ctx = ctx;
new_val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
iso, throw_ret_val);

return tracked_value(ctx, new_val);
}

/********** CpuProfiler **********/

CPUProfiler* NewCPUProfiler(IsolatePtr iso_ptr) {
Expand Down
2 changes: 2 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ extern void IsolateTerminateExecution(IsolatePtr ptr);
extern int IsolateIsExecutionTerminating(IsolatePtr ptr);
extern IsolateHStatistics IsolationGetHeapStatistics(IsolatePtr ptr);

extern ValuePtr IsolateThrowException(IsolatePtr iso, ValuePtr value);

extern RtnUnboundScript IsolateCompileUnboundScript(IsolatePtr iso_ptr,
const char* source,
const char* origin,
Expand Down

0 comments on commit d8d94c2

Please sign in to comment.