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 1 commit
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
34 changes: 34 additions & 0 deletions exception.go
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
// #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")
}
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>
}
82 changes: 82 additions & 0 deletions promise.go
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
// #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")
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}
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 {
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
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
}
77 changes: 68 additions & 9 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Context> local_ctx = val->ctx.Get(iso); \
if (local_ctx.IsEmpty()) { \
m_ctx* ctx = static_cast<m_ctx*>(iso->GetData(0)); \
Expand Down Expand Up @@ -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<Value>(iso, value->ToObject(local_ctx).ToLocalChecked()));
new_val->ptr.Reset(
iso, Persistent<Value>(iso, value->ToObject(local_ctx).ToLocalChecked()));
return static_cast<ValuePtr>(new_val);
}

Expand Down Expand Up @@ -884,13 +885,14 @@ int ValueIsModuleNamespaceObject(ValuePtr ptr) {

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

#define LOCAL_OBJECT(ptr) \
LOCAL_VALUE(ptr) \
Local<Object> obj = value.As<Object>() \
#define LOCAL_OBJECT(ptr) \
LOCAL_VALUE(ptr) \
Local<Object> obj = value.As<Object>()

void ObjectSet(ValuePtr ptr, const char* key, ValuePtr val_ptr) {
LOCAL_OBJECT(ptr);
Local<String> key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
Local<String> key_val =
String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
m_value* prop_val = static_cast<m_value*>(val_ptr);
obj->Set(local_ctx, key_val, prop_val->ptr.Get(iso)).Check();
}
Expand All @@ -905,7 +907,8 @@ RtnValue ObjectGet(ValuePtr ptr, const char* key) {
LOCAL_OBJECT(ptr);
RtnValue rtn = {nullptr, nullptr};

Local<String> key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
Local<String> key_val =
String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
MaybeLocal<Value> result = obj->Get(local_ctx, key_val);
if (result.IsEmpty()) {
rtn.error = ExceptionError(try_catch, iso, local_ctx);
Expand Down Expand Up @@ -940,7 +943,8 @@ RtnValue ObjectGetIdx(ValuePtr ptr, uint32_t idx) {

int ObjectHas(ValuePtr ptr, const char* key) {
LOCAL_OBJECT(ptr);
Local<String> key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
Local<String> key_val =
String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
return obj->Has(local_ctx, key_val).ToChecked();
}

Expand All @@ -951,7 +955,8 @@ int ObjectHasIdx(ValuePtr ptr, uint32_t idx) {

int ObjectDelete(ValuePtr ptr, const char* key) {
LOCAL_OBJECT(ptr);
Local<String> key_val = String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
Local<String> key_val =
String::NewFromUtf8(iso, key, NewStringType::kNormal).ToLocalChecked();
return obj->Delete(local_ctx, key_val).ToChecked();
}

Expand All @@ -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<Promise::Resolver> 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<Value>(iso, resolver.ToLocalChecked()));
return static_cast<ValuePtr>(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.Reset(iso, local_ctx);
promise_val->ptr.Reset(iso, Persistent<Value>(iso, promise));
return static_cast<ValuePtr>(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.Reset(iso, local_ctx);
result_val->ptr.Reset(iso, Persistent<Value>(iso, result));
return static_cast<ValuePtr>(result_val);
}

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

const char* Version() {
Expand Down
7 changes: 7 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion value.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down