Skip to content

Commit

Permalink
Support for invoking a function value (#100)
Browse files Browse the repository at this point in the history
* Support for invoking a function value

Fixes #95

* *Value -> Valuer, avoid multiple branches of FunctionCall

* Make Call variadic, add a Changelog note

Co-authored-by: Roger Chapman <[email protected]>
  • Loading branch information
robfig and rogchap authored Apr 9, 2021
1 parent 6341a3b commit 25d7afa
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Promise resolver and promise result
- Convert a Value to a Function and invoke it

### Changed
- Upgrade to V8 8.9.255.20
Expand Down
30 changes: 30 additions & 0 deletions function.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 "v8go.h"
import "C"
import (
"unsafe"
)

// Function is a JavaScript function.
type Function struct {
*Value
}

// Call this JavaScript function with the given arguments.
func (fn *Function) Call(args ...Valuer) (*Value, error) {
var argptr *C.ValuePtr
if len(args) > 0 {
var cArgs = make([]C.ValuePtr, len(args))
for i, arg := range args {
cArgs[i] = arg.value().ptr
}
argptr = (*C.ValuePtr)(unsafe.Pointer(&cArgs[0]))
}
rtn := C.FunctionCall(fn.ptr, C.int(len(args)), argptr)
return getValue(fn.ctx, rtn), getError(rtn)
}
63 changes: 63 additions & 0 deletions function_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 TestFunctionCall(t *testing.T) {
t.Parallel()

ctx, err := v8go.NewContext()
failIf(t, err)
_, err = ctx.RunScript("function add(a, b) { return a + b; }", "")
failIf(t, err)
addValue, err := ctx.Global().Get("add")
failIf(t, err)
iso, _ := ctx.Isolate()

arg1, err := v8go.NewValue(iso, int32(1))
failIf(t, err)

fn, _ := addValue.AsFunction()
resultValue, err := fn.Call(arg1, arg1)
failIf(t, err)

if resultValue.Int32() != 2 {
t.Errorf("expected 1 + 1 = 2, got: %v", resultValue.DetailString())
}
}

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

ctx, err := v8go.NewContext()
failIf(t, err)
_, err = ctx.RunScript("function throws() { throw 'error'; }", "script.js")
failIf(t, err)
addValue, err := ctx.Global().Get("throws")
failIf(t, err)

fn, _ := addValue.AsFunction()
_, err = fn.Call()
if err == nil {
t.Errorf("expected an error, got none")
}
got := *(err.(*v8go.JSError))
want := v8go.JSError{Message: "error", Location: "script.js:1:21"}
if got != want {
t.Errorf("want %+v, got: %+v", want, got)
}
}

func failIf(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatal(err)
}
}
25 changes: 25 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,31 @@ ValuePtr PromiseResult(ValuePtr ptr) {
return tracked_value(ctx, result_val);
}

/********** Function **********/

RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr args[]) {
LOCAL_VALUE(ptr)
RtnValue rtn = {nullptr, nullptr};
Local<Function> fn = Local<Function>::Cast(value);
Local<Value> argv[argc];
for (int i = 0; i < argc; i++) {
m_value* arg = static_cast<m_value*>(args[i]);
argv[i] = arg->ptr.Get(iso);
}
Local<Value> recv = Undefined(iso);
MaybeLocal<Value> result = fn->Call(local_ctx, recv, argc, argv);
if (result.IsEmpty()) {
rtn.error = ExceptionError(try_catch, iso, local_ctx);
return rtn;
}
m_value* rtnval = new m_value;
rtnval->iso = iso;
rtnval->ctx = ctx;
rtnval->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, result.ToLocalChecked());
rtn.value = tracked_value(ctx, rtnval);
return rtn;
}

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

ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) {
Expand Down
2 changes: 2 additions & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr);
int PromiseState(ValuePtr ptr);
extern ValuePtr PromiseResult(ValuePtr ptr);

extern RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr argv[]);

extern ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message);
extern ValuePtr ExceptionRangeError(IsolatePtr iso_ptr, const char* message);
extern ValuePtr ExceptionReferenceError(IsolatePtr iso_ptr,
Expand Down
7 changes: 7 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,13 @@ func (v *Value) AsPromise() (*Promise, error) {
return &Promise{&Object{v}}, nil
}

func (v *Value) AsFunction() (*Function, error) {
if !v.IsFunction() {
return nil, errors.New("v8go: value is not a Function")
}
return &Function{v}, nil
}

// MarshalJSON implements the json.Marshaler interface.
func (v *Value) MarshalJSON() ([]byte, error) {
jsonStr, err := JSONStringify(nil, v)
Expand Down
18 changes: 18 additions & 0 deletions value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,24 @@ func TestValuePromise(t *testing.T) {

}

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

ctx, _ := v8go.NewContext()
val, _ := ctx.RunScript("1", "")
if _, err := val.AsFunction(); err == nil {
t.Error("Expected error but got <nil>")
}
val, err := ctx.RunScript("(a, b) => { return a + b; }", "")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if _, err := val.AsFunction(); err != nil {
t.Errorf("Expected success but got: %v", err)
}

}

func TestValueIsXXX(t *testing.T) {
t.Parallel()
iso, _ := v8go.NewIsolate()
Expand Down

0 comments on commit 25d7afa

Please sign in to comment.