From 3e4da543b6dfad96a20165b7a0aa8989d5189e11 Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Thu, 11 Feb 2021 21:28:31 +1100 Subject: [PATCH 1/6] Promise resolver and promise result --- exception.go | 34 ++++++++++++++++ function_template_test.go | 24 +++++++++--- promise.go | 82 +++++++++++++++++++++++++++++++++++++++ v8go.cc | 77 +++++++++++++++++++++++++++++++----- v8go.h | 7 ++++ value.go | 9 ++++- 6 files changed, 218 insertions(+), 15 deletions(-) create mode 100644 exception.go create mode 100644 promise.go diff --git a/exception.go b/exception.go new file mode 100644 index 00000000..fe053fdd --- /dev/null +++ b/exception.go @@ -0,0 +1,34 @@ +// 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 + +// #include +// #include "v8go.h" +import "C" + +// Error creates a generic error message. +func Error(msg string) *Value { + panic("not implemented") +} + +// RangeError creates a range error message. +func RangeError(msg string) *Value { + panic("not implemented") +} + +// ReferenceError creates a reference error message. +func ReferenceError(msg string) *Value { + panic("not implemented") +} + +// SyntaxError creates a syntax error message. +func SyntaxError(msg string) *Value { + panic("not implemented") +} + +// TypeError creates a type error message. +func TypeError(msg string) *Value { + panic("not implemented") +} diff --git a/function_template_test.go b/function_template_test.go index 28a82107..d296e529 100644 --- a/function_template_test.go +++ b/function_template_test.go @@ -52,18 +52,32 @@ func ExampleFunctionTemplate() { func ExampleFunctionTemplate_fetch() { iso, _ := v8go.NewIsolate() global, _ := v8go.NewObjectTemplate(iso) + fetchfn, _ := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value { args := info.Args() url := args[0].String() - res, _ := http.Get(url) - body, _ := ioutil.ReadAll(res.Body) - val, _ := v8go.NewValue(iso, string(body)) - return val + + resolver, _ := v8go.NewPromiseResolver(info.Context()) + + go func() { + res, _ := http.Get(url) + body, _ := ioutil.ReadAll(res.Body) + val, _ := v8go.NewValue(iso, string(body)) + resolver.Resolve(val) + }() + return resolver.GetPromise().Value }) global.Set("fetch", fetchfn, v8go.ReadOnly) + ctx, _ := v8go.NewContext(iso, global) val, _ := ctx.RunScript("fetch('https://rogchap.com/v8go')", "") - fmt.Printf("%s\n", strings.Split(val.String(), "\n")[0]) + prom, _ := val.AsPromise() + + // wait for the promise to resolve + for prom.State() == v8go.Pending { + continue + } + fmt.Printf("%s\n", strings.Split(prom.Result().String(), "\n")[0]) // Output: // } diff --git a/promise.go b/promise.go new file mode 100644 index 00000000..7528a2cb --- /dev/null +++ b/promise.go @@ -0,0 +1,82 @@ +// 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 + +// #include +// #include "v8go.h" +import "C" +import ( + "errors" + "runtime" +) + +// PromiseState +type PromiseState int + +const ( + Pending PromiseState = iota + Fulfilled + Rejected +) + +// PromiseResolver +type PromiseResolver struct { + *Object + prom *Promise +} + +// Promise +type Promise struct { + *Object +} + +// MewPromiseResolver +func NewPromiseResolver(ctx *Context) (*PromiseResolver, error) { + if ctx == nil { + return nil, errors.New("v8go: Context is required") + } + ptr := C.NewPromiseResolver(ctx.ptr) + val := &Value{ptr, ctx} + runtime.SetFinalizer(val, (*Value).finalizer) + return &PromiseResolver{&Object{val}, nil}, nil +} + +// GetPromise +func (r *PromiseResolver) GetPromise() *Promise { + if r.prom == nil { + ptr := C.PromiseResolverGetPromise(r.ptr) + val := &Value{ptr, r.ctx} + runtime.SetFinalizer(val, (*Value).finalizer) + r.prom = &Promise{&Object{val}} + } + return r.prom +} + +// Resolve +func (r *PromiseResolver) Resolve(val Valuer) bool { + r.ctx.register() + defer r.ctx.deregister() + return C.PromiseResolverResolve(r.ptr, val.value().ptr) != 0 +} + +// Reject +func (r *PromiseResolver) Reject(err *Value) bool { + r.ctx.register() + defer r.ctx.deregister() + return C.PromiseResolverReject(r.ptr, err.ptr) != 0 +} + +// State +func (p *Promise) State() PromiseState { + return PromiseState(C.PromiseState(p.ptr)) +} + +// Result +func (p *Promise) Result() *Value { + ptr := C.PromiseResult(p.ptr) + val := &Value{ptr, p.ctx} + runtime.SetFinalizer(val, (*Value).finalizer) + return val +} diff --git a/v8go.cc b/v8go.cc index 6478e1ad..4013e112 100644 --- a/v8go.cc +++ b/v8go.cc @@ -450,7 +450,7 @@ ValuePtr ContextGlobal(ContextPtr ctx_ptr) { Locker locker(iso); \ Isolate::Scope isolate_scope(iso); \ HandleScope handle_scope(iso); \ - TryCatch try_catch(iso); \ + TryCatch try_catch(iso); \ Local local_ctx = val->ctx.Get(iso); \ if (local_ctx.IsEmpty()) { \ m_ctx* ctx = static_cast(iso->GetData(0)); \ @@ -608,7 +608,8 @@ ValuePtr ValueToObject(ValuePtr ptr) { m_value* new_val = new m_value; new_val->iso = iso; new_val->ctx.Reset(iso, local_ctx); - new_val->ptr.Reset(iso, Persistent(iso, value->ToObject(local_ctx).ToLocalChecked())); + new_val->ptr.Reset( + iso, Persistent(iso, value->ToObject(local_ctx).ToLocalChecked())); return static_cast(new_val); } @@ -884,13 +885,14 @@ int ValueIsModuleNamespaceObject(ValuePtr ptr) { /********** Object **********/ -#define LOCAL_OBJECT(ptr) \ - LOCAL_VALUE(ptr) \ - Local obj = value.As() \ +#define LOCAL_OBJECT(ptr) \ + LOCAL_VALUE(ptr) \ + Local obj = value.As() void ObjectSet(ValuePtr ptr, const char* key, ValuePtr val_ptr) { LOCAL_OBJECT(ptr); - Local key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); + Local key_val = + String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); m_value* prop_val = static_cast(val_ptr); obj->Set(local_ctx, key_val, prop_val->ptr.Get(iso)).Check(); } @@ -905,7 +907,8 @@ RtnValue ObjectGet(ValuePtr ptr, const char* key) { LOCAL_OBJECT(ptr); RtnValue rtn = {nullptr, nullptr}; - Local key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); + Local key_val = + String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); MaybeLocal result = obj->Get(local_ctx, key_val); if (result.IsEmpty()) { rtn.error = ExceptionError(try_catch, iso, local_ctx); @@ -940,7 +943,8 @@ RtnValue ObjectGetIdx(ValuePtr ptr, uint32_t idx) { int ObjectHas(ValuePtr ptr, const char* key) { LOCAL_OBJECT(ptr); - Local key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); + Local key_val = + String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); return obj->Has(local_ctx, key_val).ToChecked(); } @@ -951,7 +955,8 @@ int ObjectHasIdx(ValuePtr ptr, uint32_t idx) { int ObjectDelete(ValuePtr ptr, const char* key) { LOCAL_OBJECT(ptr); - Local key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); + Local key_val = + String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked(); return obj->Delete(local_ctx, key_val).ToChecked(); } @@ -960,6 +965,60 @@ int ObjectDeleteIdx(ValuePtr ptr, uint32_t idx) { return obj->Delete(local_ctx, idx).ToChecked(); } +/********** Promise **********/ + +ValuePtr NewPromiseResolver(ContextPtr ctx_ptr) { + LOCAL_CONTEXT(ctx_ptr); + MaybeLocal resolver = Promise::Resolver::New(local_ctx); + m_value* val = new m_value; + val->iso = iso; + val->ctx.Reset(iso, local_ctx); + val->ptr.Reset(iso, Persistent(iso, resolver.ToLocalChecked())); + return static_cast(val); +} + +ValuePtr PromiseResolverGetPromise(ValuePtr ptr) { + LOCAL_VALUE(ptr); + Local resolver = value.As(); + Local promise = resolver->GetPromise(); + m_value* promise_val = new m_value; + promise_val->iso = iso; + promise_val->ctx.Reset(iso, local_ctx); + promise_val->ptr.Reset(iso, Persistent(iso, promise)); + return static_cast(promise_val); +} + +int PromiseResolverResolve(ValuePtr ptr, ValuePtr val_ptr) { + LOCAL_VALUE(ptr); + Local resolver = value.As(); + m_value* resolve_val = static_cast(val_ptr); + return resolver->Resolve(local_ctx, resolve_val->ptr.Get(iso)).ToChecked(); +} + +int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr) { + LOCAL_VALUE(ptr); + Local resolver = value.As(); + m_value* reject_val = static_cast(val_ptr); + return resolver->Reject(local_ctx, reject_val->ptr.Get(iso)).ToChecked(); +} + +int PromiseState(ValuePtr ptr) { + LOCAL_VALUE(ptr) + Local promise = value.As(); + return promise->State(); +} + +ValuePtr PromiseResult(ValuePtr ptr) { + LOCAL_VALUE(ptr) + Local promise = value.As(); + Local result = promise->Result(); + m_value* result_val = new m_value; + result_val->iso = iso; + result_val->ctx.Reset(iso, local_ctx); + result_val->ptr.Reset(iso, Persistent(iso, result)); + return static_cast(result_val); +} + /********** Version **********/ const char* Version() { diff --git a/v8go.h b/v8go.h index 5db7342a..044d234b 100644 --- a/v8go.h +++ b/v8go.h @@ -165,6 +165,13 @@ int ObjectHasIdx(ValuePtr ptr, uint32_t idx); int ObjectDelete(ValuePtr ptr, const char* key); int ObjectDeleteIdx(ValuePtr ptr, uint32_t idx); +extern ValuePtr NewPromiseResolver(ContextPtr ctx_ptr); +extern ValuePtr PromiseResolverGetPromise(ValuePtr ptr); +int PromiseResolverResolve(ValuePtr ptr, ValuePtr val_ptr); +int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr); +int PromiseState(ValuePtr ptr); +extern ValuePtr PromiseResult(ValuePtr ptr); + const char* Version(); #ifdef __cplusplus diff --git a/value.go b/value.go index c31c2599..84f6c7a6 100644 --- a/value.go +++ b/value.go @@ -518,12 +518,19 @@ func (v *Value) IsModuleNamespaceObject() bool { // then an error is returned. Use `value.Object()` to do the JS equivalent of `Object(value)`. func (v *Value) AsObject() (*Object, error) { if !v.IsObject() { - return nil, errors.New("value: unable to cast to Object; value is not an Object") + return nil, errors.New("v8go: value is not an Object") } return &Object{v}, nil } +func (v *Value) AsPromise() (*Promise, error) { + if !v.IsPromise() { + return nil, errors.New("v8go: value is not a Promise") + } + return &Promise{&Object{v}}, nil +} + func (v *Value) finalizer() { C.ValueFree(v.ptr) v.ptr = nil From 9675c5e4b6bab891977c4f51f7d03ced6c263872 Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Fri, 19 Feb 2021 14:38:17 +1100 Subject: [PATCH 2/6] update persistent value to match master --- v8go.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/v8go.cc b/v8go.cc index 76fba5c9..95a783d0 100644 --- a/v8go.cc +++ b/v8go.cc @@ -968,7 +968,7 @@ RtnValue ObjectGet(ValuePtr ptr, const char* key) { m_value* new_val = new m_value; new_val->iso = iso; new_val->ctx = ctx; - new_val->ptr.Reset(iso, Persistent(iso, result.ToLocalChecked())); + new_val->ptr = Persistent>(iso, result.ToLocalChecked()); rtn.value = tracked_value(ctx, new_val); return rtn; @@ -1024,9 +1024,9 @@ ValuePtr NewPromiseResolver(ContextPtr ctx_ptr) { MaybeLocal resolver = Promise::Resolver::New(local_ctx); m_value* val = new m_value; val->iso = iso; - val->ctx.Reset(iso, local_ctx); - val->ptr.Reset(iso, Persistent(iso, resolver.ToLocalChecked())); - return static_cast(val); + val->ctx = ctx; + val->ptr = Persistent>(iso, resolver.ToLocalChecked()); + return tracked_value(ctx, val); } ValuePtr PromiseResolverGetPromise(ValuePtr ptr) { @@ -1035,9 +1035,9 @@ ValuePtr PromiseResolverGetPromise(ValuePtr ptr) { Local promise = resolver->GetPromise(); m_value* promise_val = new m_value; promise_val->iso = iso; - promise_val->ctx.Reset(iso, local_ctx); - promise_val->ptr.Reset(iso, Persistent(iso, promise)); - return static_cast(promise_val); + promise_val->ctx = ctx; + promise_val->ptr = Persistent>(iso, promise); + return tracked_value(ctx, promise_val); } int PromiseResolverResolve(ValuePtr ptr, ValuePtr val_ptr) { @@ -1066,9 +1066,9 @@ ValuePtr PromiseResult(ValuePtr ptr) { Local result = promise->Result(); m_value* result_val = new m_value; result_val->iso = iso; - result_val->ctx.Reset(iso, local_ctx); - result_val->ptr.Reset(iso, Persistent(iso, result)); - return static_cast(result_val); + result_val->ctx = ctx; + result_val->ptr = Persistent>(iso, result); + return tracked_value(ctx, result_val); } /********** Version **********/ From bccfa1deebcbcd9e3eee730b157c4e71c6eaf206 Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Tue, 2 Mar 2021 20:56:10 +1100 Subject: [PATCH 3/6] Add docs, test and Exception types --- exception.go | 51 ++++++++++++++++++++++++++++++++--------- promise.go | 31 ++++++++++++++----------- promise_test.go | 51 +++++++++++++++++++++++++++++++++++++++++ v8go.cc | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ v8go.h | 6 +++++ 5 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 promise_test.go diff --git a/exception.go b/exception.go index fe053fdd..975df60f 100644 --- a/exception.go +++ b/exception.go @@ -7,28 +7,59 @@ package v8go // #include // #include "v8go.h" import "C" +import ( + "fmt" + "runtime" + "unsafe" +) // Error creates a generic error message. -func Error(msg string) *Value { - panic("not implemented") +func Error(iso *Isolate, msg string) *Value { + fmt.Printf("iso = %+v\n", iso) + cstr := C.CString(msg) + defer C.free(unsafe.Pointer(cstr)) + ptr := C.ExceptionError(iso.ptr, cstr) + v := &Value{ptr: ptr} + runtime.SetFinalizer(v, (*Value).finalizer) + return v } // RangeError creates a range error message. -func RangeError(msg string) *Value { - panic("not implemented") +func RangeError(iso *Isolate, msg string) *Value { + cstr := C.CString(msg) + defer C.free(unsafe.Pointer(cstr)) + ptr := C.ExceptionRangeError(iso.ptr, cstr) + v := &Value{ptr: ptr} + runtime.SetFinalizer(v, (*Value).finalizer) + return v } // ReferenceError creates a reference error message. -func ReferenceError(msg string) *Value { - panic("not implemented") +func ReferenceError(iso *Isolate, msg string) *Value { + cstr := C.CString(msg) + defer C.free(unsafe.Pointer(cstr)) + ptr := C.ExceptionReferenceError(iso.ptr, cstr) + v := &Value{ptr: ptr} + runtime.SetFinalizer(v, (*Value).finalizer) + return v } // SyntaxError creates a syntax error message. -func SyntaxError(msg string) *Value { - panic("not implemented") +func SyntaxError(iso *Isolate, msg string) *Value { + cstr := C.CString(msg) + defer C.free(unsafe.Pointer(cstr)) + ptr := C.ExceptionSyntaxError(iso.ptr, cstr) + v := &Value{ptr: ptr} + runtime.SetFinalizer(v, (*Value).finalizer) + return v } // TypeError creates a type error message. -func TypeError(msg string) *Value { - panic("not implemented") +func TypeError(iso *Isolate, msg string) *Value { + cstr := C.CString(msg) + defer C.free(unsafe.Pointer(cstr)) + ptr := C.ExceptionTypeError(iso.ptr, cstr) + v := &Value{ptr: ptr} + runtime.SetFinalizer(v, (*Value).finalizer) + return v } diff --git a/promise.go b/promise.go index 7528a2cb..c1fae698 100644 --- a/promise.go +++ b/promise.go @@ -9,10 +9,9 @@ package v8go import "C" import ( "errors" - "runtime" ) -// PromiseState +// PromiseState is the state of the Promise. type PromiseState int const ( @@ -21,62 +20,68 @@ const ( Rejected ) -// PromiseResolver +// PromiseResolver is the resolver object for the promise. +// Most cases will create a new PromiseResolver and return +// the associated Promise from the resolver. type PromiseResolver struct { *Object prom *Promise } -// Promise +// Promise is the JavaScript promise object defined in ES6 type Promise struct { *Object } -// MewPromiseResolver +// MewPromiseResolver creates a new Promise resolver for the given context. +// The associated Promise will be in a Pending state. func NewPromiseResolver(ctx *Context) (*PromiseResolver, error) { if ctx == nil { return nil, errors.New("v8go: Context is required") } ptr := C.NewPromiseResolver(ctx.ptr) val := &Value{ptr, ctx} - runtime.SetFinalizer(val, (*Value).finalizer) return &PromiseResolver{&Object{val}, nil}, nil } -// GetPromise +// GetPromise returns the associated Promise object for this resolver. +// The Promise object is unique to the resolver and returns the same object +// on multiple calls. func (r *PromiseResolver) GetPromise() *Promise { if r.prom == nil { ptr := C.PromiseResolverGetPromise(r.ptr) val := &Value{ptr, r.ctx} - runtime.SetFinalizer(val, (*Value).finalizer) r.prom = &Promise{&Object{val}} } return r.prom } -// Resolve +// Resolve invokes the Promise resolve state with the given value. +// The Promise state will transition from Pending to Fulfilled. func (r *PromiseResolver) Resolve(val Valuer) bool { r.ctx.register() defer r.ctx.deregister() return C.PromiseResolverResolve(r.ptr, val.value().ptr) != 0 } -// Reject +// Reject invokes the Promise reject state with the given value. +// The Promise state will transition from Pending to Rejected. func (r *PromiseResolver) Reject(err *Value) bool { r.ctx.register() defer r.ctx.deregister() return C.PromiseResolverReject(r.ptr, err.ptr) != 0 } -// State +// State returns the current state of the Promise. func (p *Promise) State() PromiseState { return PromiseState(C.PromiseState(p.ptr)) } -// Result +// Result is the value result of the Promise. The Promise must +// NOT be in a Pending state, otherwise may panic. Call promise.State() +// to validate state before calling for the result. func (p *Promise) Result() *Value { ptr := C.PromiseResult(p.ptr) val := &Value{ptr, p.ctx} - runtime.SetFinalizer(val, (*Value).finalizer) return val } diff --git a/promise_test.go b/promise_test.go new file mode 100644 index 00000000..347fe889 --- /dev/null +++ b/promise_test.go @@ -0,0 +1,51 @@ +// 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 ( + "fmt" + "testing" + + "rogchap.com/v8go" +) + +func TestPromise(t *testing.T) { + t.Parallel() + + iso, _ := v8go.NewIsolate() + ctx, _ := v8go.NewContext(iso) + if _, err := v8go.NewPromiseResolver(nil); err == nil { + t.Error("expected error with Context") + } + + res1, _ := v8go.NewPromiseResolver(ctx) + prom1 := res1.GetPromise() + if s := prom1.State(); s != v8go.Pending { + t.Errorf("unexpected state for Promise, want Pending (0) got: %v", s) + } + + val1, _ := v8go.NewValue(iso, "foo") + res1.Resolve(val1) + + if s := prom1.State(); s != v8go.Fulfilled { + t.Fatalf("unexpected state for Promise, want Fulfilled (1) got: %v", s) + } + + if result := prom1.Result(); result.String() != val1.String() { + t.Errorf("expected the Promise result to match the resolve value, but got: %s", result) + } + + res2, _ := v8go.NewPromiseResolver(ctx) + val2 := v8go.Error(iso, "Bad Foo") + res2.Reject(val2) + + prom2 := res2.GetPromise() + if s := prom2.State(); s != v8go.Rejected { + t.Fatalf("unexpected state for Promise, want Rejected (2) got: %v", s) + } + + fmt.Printf("prom2.Result() = %+v\n", prom2.Result()) + +} diff --git a/v8go.cc b/v8go.cc index 95a783d0..1b7f7ca3 100644 --- a/v8go.cc +++ b/v8go.cc @@ -506,6 +506,7 @@ ValuePtr NewValueInteger(IsolatePtr iso_ptr, int32_t v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, Integer::New(iso, v)); return static_cast(val); @@ -515,6 +516,7 @@ ValuePtr NewValueIntegerFromUnsigned(IsolatePtr iso_ptr, uint32_t v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, Integer::NewFromUnsigned(iso, v)); return static_cast(val); @@ -524,6 +526,7 @@ ValuePtr NewValueString(IsolatePtr iso_ptr, const char* v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, String::NewFromUtf8(iso, v).ToLocalChecked()); return static_cast(val); @@ -533,6 +536,7 @@ ValuePtr NewValueBoolean(IsolatePtr iso_ptr, int v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, Boolean::New(iso, v)); return static_cast(val); @@ -542,6 +546,7 @@ ValuePtr NewValueNumber(IsolatePtr iso_ptr, double v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, Number::New(iso, v)); return static_cast(val); @@ -551,6 +556,7 @@ ValuePtr NewValueBigInt(IsolatePtr iso_ptr, int64_t v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, BigInt::New(iso, v)); return static_cast(val); @@ -560,6 +566,7 @@ ValuePtr NewValueBigIntFromUnsigned(IsolatePtr iso_ptr, uint64_t v) { ISOLATE_SCOPE(iso_ptr); m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; val->ptr = Persistent>( iso, BigInt::NewFromUnsigned(iso, v)); return static_cast(val); @@ -574,6 +581,7 @@ ValuePtr NewValueBigIntFromWords(IsolatePtr iso_ptr, m_value* val = new m_value; val->iso = iso; + val->ctx = nullptr; MaybeLocal bigint = BigInt::NewFromWords(ctx->ptr.Get(iso), sign_bit, word_count, words); val->ptr = Persistent>( @@ -1071,6 +1079,58 @@ ValuePtr PromiseResult(ValuePtr ptr) { return tracked_value(ctx, result_val); } +/******** Exceptions *********/ + +ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) { + ISOLATE_SCOPE(iso_ptr); + Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + m_value* val = new m_value; + val->iso = iso; + val->ctx = nullptr; + val->ptr = Persistent>(iso, Exception::Error(msg)); + return static_cast(val); +} + +ValuePtr ExceptionRangeError(IsolatePtr iso_ptr, const char* message) { + ISOLATE_SCOPE(iso_ptr); + Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + m_value* val = new m_value; + val->iso = iso; + val->ctx = nullptr; + val->ptr = Persistent>(iso, Exception::RangeError(msg)); + return static_cast(val); +} + +ValuePtr ExceptionReferenceError(IsolatePtr iso_ptr, const char* message) { + ISOLATE_SCOPE(iso_ptr); + Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + m_value* val = new m_value; + val->iso = iso; + val->ctx = nullptr; + val->ptr = Persistent>(iso, Exception::ReferenceError(msg)); + return static_cast(val); +} + +ValuePtr ExceptionSyntaxError(IsolatePtr iso_ptr, const char* message) { + ISOLATE_SCOPE(iso_ptr); + Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + m_value* val = new m_value; + val->iso = iso; + val->ctx = nullptr; + val->ptr = Persistent>(iso, Exception::SyntaxError(msg)); + return static_cast(val); +} + +ValuePtr ExceptionTypeError(IsolatePtr iso_ptr, const char* message) { + ISOLATE_SCOPE(iso_ptr); + Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + m_value* val = new m_value; + val->iso = iso; + val->ctx = nullptr; + val->ptr = Persistent>(iso, Exception::TypeError(msg)); + return static_cast(val); +} + /********** Version **********/ const char* Version() { diff --git a/v8go.h b/v8go.h index 59aeddca..b01975fc 100644 --- a/v8go.h +++ b/v8go.h @@ -173,6 +173,12 @@ int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr); int PromiseState(ValuePtr ptr); extern ValuePtr PromiseResult(ValuePtr ptr); +extern ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message); +extern ValuePtr ExceptionRangeError(IsolatePtr iso_ptr, const char* message); +extern ValuePtr ExceptionReferenceError(IsolatePtr iso_ptr, const char* message); +extern ValuePtr ExceptionSyntaxError(IsolatePtr iso_ptr, const char* message); +extern ValuePtr ExceptionTypeError(IsolatePtr iso_ptr, const char* message); + const char* Version(); #ifdef __cplusplus From 649ed14ce5e8842f124edae4f11a84773b0ba7b2 Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Tue, 2 Mar 2021 22:21:48 +1100 Subject: [PATCH 4/6] remove the exception error functions --- exception.go | 65 ------------------------------------------------- promise_test.go | 6 +---- v8go.cc | 4 ++- 3 files changed, 4 insertions(+), 71 deletions(-) delete mode 100644 exception.go diff --git a/exception.go b/exception.go deleted file mode 100644 index 975df60f..00000000 --- a/exception.go +++ /dev/null @@ -1,65 +0,0 @@ -// 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 - -// #include -// #include "v8go.h" -import "C" -import ( - "fmt" - "runtime" - "unsafe" -) - -// Error creates a generic error message. -func Error(iso *Isolate, msg string) *Value { - fmt.Printf("iso = %+v\n", iso) - cstr := C.CString(msg) - defer C.free(unsafe.Pointer(cstr)) - ptr := C.ExceptionError(iso.ptr, cstr) - v := &Value{ptr: ptr} - runtime.SetFinalizer(v, (*Value).finalizer) - return v -} - -// RangeError creates a range error message. -func RangeError(iso *Isolate, msg string) *Value { - cstr := C.CString(msg) - defer C.free(unsafe.Pointer(cstr)) - ptr := C.ExceptionRangeError(iso.ptr, cstr) - v := &Value{ptr: ptr} - runtime.SetFinalizer(v, (*Value).finalizer) - return v -} - -// ReferenceError creates a reference error message. -func ReferenceError(iso *Isolate, msg string) *Value { - cstr := C.CString(msg) - defer C.free(unsafe.Pointer(cstr)) - ptr := C.ExceptionReferenceError(iso.ptr, cstr) - v := &Value{ptr: ptr} - runtime.SetFinalizer(v, (*Value).finalizer) - return v -} - -// SyntaxError creates a syntax error message. -func SyntaxError(iso *Isolate, msg string) *Value { - cstr := C.CString(msg) - defer C.free(unsafe.Pointer(cstr)) - ptr := C.ExceptionSyntaxError(iso.ptr, cstr) - v := &Value{ptr: ptr} - runtime.SetFinalizer(v, (*Value).finalizer) - return v -} - -// TypeError creates a type error message. -func TypeError(iso *Isolate, msg string) *Value { - cstr := C.CString(msg) - defer C.free(unsafe.Pointer(cstr)) - ptr := C.ExceptionTypeError(iso.ptr, cstr) - v := &Value{ptr: ptr} - runtime.SetFinalizer(v, (*Value).finalizer) - return v -} diff --git a/promise_test.go b/promise_test.go index 347fe889..1ad38ed8 100644 --- a/promise_test.go +++ b/promise_test.go @@ -5,7 +5,6 @@ package v8go_test import ( - "fmt" "testing" "rogchap.com/v8go" @@ -38,14 +37,11 @@ func TestPromise(t *testing.T) { } res2, _ := v8go.NewPromiseResolver(ctx) - val2 := v8go.Error(iso, "Bad Foo") + val2, _ := v8go.NewValue(iso, "Bad Foo") res2.Reject(val2) prom2 := res2.GetPromise() if s := prom2.State(); s != v8go.Rejected { t.Fatalf("unexpected state for Promise, want Rejected (2) got: %v", s) } - - fmt.Printf("prom2.Result() = %+v\n", prom2.Result()) - } diff --git a/v8go.cc b/v8go.cc index 1b7f7ca3..64be3f3c 100644 --- a/v8go.cc +++ b/v8go.cc @@ -1083,10 +1083,12 @@ ValuePtr PromiseResult(ValuePtr ptr) { ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) { ISOLATE_SCOPE(iso_ptr); - Local msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked(); + Local msg = String::NewFromUtf8(iso, message).ToLocalChecked(); m_value* val = new m_value; val->iso = iso; val->ctx = nullptr; + // TODO(rogchap): This currently causes a segfault, and I'm not sure why! + // Even a simple error with an empty string causes the error: Exception::Error(String::Empty(iso)) val->ptr = Persistent>(iso, Exception::Error(msg)); return static_cast(val); } From 1944a6e1d898127d9171e966425922fb311331aa Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Tue, 2 Mar 2021 22:30:11 +1100 Subject: [PATCH 5/6] add test for when value is not a promise --- value_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/value_test.go b/value_test.go index ae966a8c..37fbd73f 100644 --- a/value_test.go +++ b/value_test.go @@ -364,6 +364,19 @@ func TestValueObject(t *testing.T) { if obj := val.Object(); obj.String() != "1" { t.Errorf("unexpected object value: %v", obj) } +} + +func TestValuePromise(t *testing.T) { + t.Parallel() + + ctx, _ := v8go.NewContext() + val, _ := ctx.RunScript("1", "") + if _, err := val.AsPromise(); err == nil { + t.Error("Expected error but got ") + } + if _, err := ctx.RunScript("new Promise(()=>{})", ""); err != nil { + t.Errorf("Unexpected error: %v", err) + } } From d233d29b10aa8aeeb551b03c4f7c863490d41802 Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Mon, 8 Mar 2021 10:24:51 +1100 Subject: [PATCH 6/6] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8c5adc..2f4a4445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Promise resolver and promise result + ## [v0.5.1] - 2021-02-19 ### Fixed