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

Promise resolver and promise result #76

Merged
merged 7 commits into from
Mar 7, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions function_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// <!DOCTYPE html>
}
87 changes: 87 additions & 0 deletions promise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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 <stdlib.h>
// #include "v8go.h"
import "C"
import (
"errors"
)

// PromiseState is the state of the Promise.
type PromiseState int

const (
Pending PromiseState = iota
Fulfilled
Rejected
)

// 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 is the JavaScript promise object defined in ES6
type Promise struct {
*Object
}

// 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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nite: lowercase?

}
ptr := C.NewPromiseResolver(ctx.ptr)
val := &Value{ptr, ctx}
return &PromiseResolver{&Object{val}, nil}, nil
}

// 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}
r.prom = &Promise{&Object{val}}
}
return r.prom
}

// 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 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not change this to Valuer?
So that we can reject an object here

r.ctx.register()
defer r.ctx.deregister()
return C.PromiseResolverReject(r.ptr, err.ptr) != 0
}

// State returns the current state of the Promise.
func (p *Promise) State() PromiseState {
return PromiseState(C.PromiseState(p.ptr))
}

// 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}
return val
}
47 changes: 47 additions & 0 deletions promise_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 (
"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 <nil> 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.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)
}
}
118 changes: 117 additions & 1 deletion v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, Integer::New(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, Integer::NewFromUnsigned(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, String::NewFromUtf8(iso, v).ToLocalChecked());
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, Boolean::New(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, Number::New(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, BigInt::New(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -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<Value, CopyablePersistentTraits<Value>>(
iso, BigInt::NewFromUnsigned(iso, v));
return static_cast<ValuePtr>(val);
Expand All @@ -574,6 +581,7 @@ ValuePtr NewValueBigIntFromWords(IsolatePtr iso_ptr,

m_value* val = new m_value;
val->iso = iso;
val->ctx = nullptr;
MaybeLocal<BigInt> bigint =
BigInt::NewFromWords(ctx->ptr.Get(iso), sign_bit, word_count, words);
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
Expand Down Expand Up @@ -968,7 +976,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<Value>(iso, result.ToLocalChecked()));
new_val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, result.ToLocalChecked());

rtn.value = tracked_value(ctx, new_val);
return rtn;
Expand Down Expand Up @@ -1017,6 +1025,114 @@ 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<Promise::Resolver> resolver = Promise::Resolver::New(local_ctx);
m_value* val = new m_value;
val->iso = iso;
val->ctx = ctx;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, resolver.ToLocalChecked());
return tracked_value(ctx, val);
}

ValuePtr PromiseResolverGetPromise(ValuePtr ptr) {
LOCAL_VALUE(ptr);
Local<Promise::Resolver> resolver = value.As<Promise::Resolver>();
Local<Promise> promise = resolver->GetPromise();
m_value* promise_val = new m_value;
promise_val->iso = iso;
promise_val->ctx = ctx;
promise_val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, promise);
return tracked_value(ctx, promise_val);
}

int PromiseResolverResolve(ValuePtr ptr, ValuePtr val_ptr) {
LOCAL_VALUE(ptr);
Local<Promise::Resolver> resolver = value.As<Promise::Resolver>();
m_value* resolve_val = static_cast<m_value*>(val_ptr);
return resolver->Resolve(local_ctx, resolve_val->ptr.Get(iso)).ToChecked();
}

int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr) {
LOCAL_VALUE(ptr);
Local<Promise::Resolver> resolver = value.As<Promise::Resolver>();
m_value* reject_val = static_cast<m_value*>(val_ptr);
return resolver->Reject(local_ctx, reject_val->ptr.Get(iso)).ToChecked();
}

int PromiseState(ValuePtr ptr) {
LOCAL_VALUE(ptr)
Local<Promise> promise = value.As<Promise>();
return promise->State();
}

ValuePtr PromiseResult(ValuePtr ptr) {
LOCAL_VALUE(ptr)
Local<Promise> promise = value.As<Promise>();
Local<Value> result = promise->Result();
m_value* result_val = new m_value;
result_val->iso = iso;
result_val->ctx = ctx;
result_val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, result);
return tracked_value(ctx, result_val);
}

/******** Exceptions *********/

ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) {
ISOLATE_SCOPE(iso_ptr);
Local<String> 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<Value, CopyablePersistentTraits<Value>>(iso, Exception::Error(msg));
return static_cast<ValuePtr>(val);
}

ValuePtr ExceptionRangeError(IsolatePtr iso_ptr, const char* message) {
ISOLATE_SCOPE(iso_ptr);
Local<String> msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked();
m_value* val = new m_value;
val->iso = iso;
val->ctx = nullptr;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, Exception::RangeError(msg));
return static_cast<ValuePtr>(val);
}

ValuePtr ExceptionReferenceError(IsolatePtr iso_ptr, const char* message) {
ISOLATE_SCOPE(iso_ptr);
Local<String> msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked();
m_value* val = new m_value;
val->iso = iso;
val->ctx = nullptr;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, Exception::ReferenceError(msg));
return static_cast<ValuePtr>(val);
}

ValuePtr ExceptionSyntaxError(IsolatePtr iso_ptr, const char* message) {
ISOLATE_SCOPE(iso_ptr);
Local<String> msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked();
m_value* val = new m_value;
val->iso = iso;
val->ctx = nullptr;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, Exception::SyntaxError(msg));
return static_cast<ValuePtr>(val);
}

ValuePtr ExceptionTypeError(IsolatePtr iso_ptr, const char* message) {
ISOLATE_SCOPE(iso_ptr);
Local<String> msg = String::NewFromUtf8(iso, message, NewStringType::kNormal).ToLocalChecked();
m_value* val = new m_value;
val->iso = iso;
val->ctx = nullptr;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, Exception::TypeError(msg));
return static_cast<ValuePtr>(val);
}

/********** Version **********/

const char* Version() {
Expand Down
13 changes: 13 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,19 @@ 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);

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
Expand Down
Loading