Skip to content

Commit 25d7afa

Browse files
robfigrogchap
andauthored
Support for invoking a function value (#100)
* 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 <rogchap@gmail.com>
1 parent 6341a3b commit 25d7afa

File tree

7 files changed

+146
-0
lines changed

7 files changed

+146
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Added
1010
- Promise resolver and promise result
11+
- Convert a Value to a Function and invoke it
1112

1213
### Changed
1314
- Upgrade to V8 8.9.255.20

function.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2021 Roger Chapman and the v8go contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package v8go
6+
7+
// #include "v8go.h"
8+
import "C"
9+
import (
10+
"unsafe"
11+
)
12+
13+
// Function is a JavaScript function.
14+
type Function struct {
15+
*Value
16+
}
17+
18+
// Call this JavaScript function with the given arguments.
19+
func (fn *Function) Call(args ...Valuer) (*Value, error) {
20+
var argptr *C.ValuePtr
21+
if len(args) > 0 {
22+
var cArgs = make([]C.ValuePtr, len(args))
23+
for i, arg := range args {
24+
cArgs[i] = arg.value().ptr
25+
}
26+
argptr = (*C.ValuePtr)(unsafe.Pointer(&cArgs[0]))
27+
}
28+
rtn := C.FunctionCall(fn.ptr, C.int(len(args)), argptr)
29+
return getValue(fn.ctx, rtn), getError(rtn)
30+
}

function_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2021 Roger Chapman and the v8go contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package v8go_test
6+
7+
import (
8+
"testing"
9+
10+
"rogchap.com/v8go"
11+
)
12+
13+
func TestFunctionCall(t *testing.T) {
14+
t.Parallel()
15+
16+
ctx, err := v8go.NewContext()
17+
failIf(t, err)
18+
_, err = ctx.RunScript("function add(a, b) { return a + b; }", "")
19+
failIf(t, err)
20+
addValue, err := ctx.Global().Get("add")
21+
failIf(t, err)
22+
iso, _ := ctx.Isolate()
23+
24+
arg1, err := v8go.NewValue(iso, int32(1))
25+
failIf(t, err)
26+
27+
fn, _ := addValue.AsFunction()
28+
resultValue, err := fn.Call(arg1, arg1)
29+
failIf(t, err)
30+
31+
if resultValue.Int32() != 2 {
32+
t.Errorf("expected 1 + 1 = 2, got: %v", resultValue.DetailString())
33+
}
34+
}
35+
36+
func TestFunctionCallError(t *testing.T) {
37+
t.Parallel()
38+
39+
ctx, err := v8go.NewContext()
40+
failIf(t, err)
41+
_, err = ctx.RunScript("function throws() { throw 'error'; }", "script.js")
42+
failIf(t, err)
43+
addValue, err := ctx.Global().Get("throws")
44+
failIf(t, err)
45+
46+
fn, _ := addValue.AsFunction()
47+
_, err = fn.Call()
48+
if err == nil {
49+
t.Errorf("expected an error, got none")
50+
}
51+
got := *(err.(*v8go.JSError))
52+
want := v8go.JSError{Message: "error", Location: "script.js:1:21"}
53+
if got != want {
54+
t.Errorf("want %+v, got: %+v", want, got)
55+
}
56+
}
57+
58+
func failIf(t *testing.T, err error) {
59+
t.Helper()
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
}

v8go.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,31 @@ ValuePtr PromiseResult(ValuePtr ptr) {
10841084
return tracked_value(ctx, result_val);
10851085
}
10861086

1087+
/********** Function **********/
1088+
1089+
RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr args[]) {
1090+
LOCAL_VALUE(ptr)
1091+
RtnValue rtn = {nullptr, nullptr};
1092+
Local<Function> fn = Local<Function>::Cast(value);
1093+
Local<Value> argv[argc];
1094+
for (int i = 0; i < argc; i++) {
1095+
m_value* arg = static_cast<m_value*>(args[i]);
1096+
argv[i] = arg->ptr.Get(iso);
1097+
}
1098+
Local<Value> recv = Undefined(iso);
1099+
MaybeLocal<Value> result = fn->Call(local_ctx, recv, argc, argv);
1100+
if (result.IsEmpty()) {
1101+
rtn.error = ExceptionError(try_catch, iso, local_ctx);
1102+
return rtn;
1103+
}
1104+
m_value* rtnval = new m_value;
1105+
rtnval->iso = iso;
1106+
rtnval->ctx = ctx;
1107+
rtnval->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, result.ToLocalChecked());
1108+
rtn.value = tracked_value(ctx, rtnval);
1109+
return rtn;
1110+
}
1111+
10871112
/******** Exceptions *********/
10881113

10891114
ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) {

v8go.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ int PromiseResolverReject(ValuePtr ptr, ValuePtr val_ptr);
173173
int PromiseState(ValuePtr ptr);
174174
extern ValuePtr PromiseResult(ValuePtr ptr);
175175

176+
extern RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr argv[]);
177+
176178
extern ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message);
177179
extern ValuePtr ExceptionRangeError(IsolatePtr iso_ptr, const char* message);
178180
extern ValuePtr ExceptionReferenceError(IsolatePtr iso_ptr,

value.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,13 @@ func (v *Value) AsPromise() (*Promise, error) {
528528
return &Promise{&Object{v}}, nil
529529
}
530530

531+
func (v *Value) AsFunction() (*Function, error) {
532+
if !v.IsFunction() {
533+
return nil, errors.New("v8go: value is not a Function")
534+
}
535+
return &Function{v}, nil
536+
}
537+
531538
// MarshalJSON implements the json.Marshaler interface.
532539
func (v *Value) MarshalJSON() ([]byte, error) {
533540
jsonStr, err := JSONStringify(nil, v)

value_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,24 @@ func TestValuePromise(t *testing.T) {
395395

396396
}
397397

398+
func TestValueFunction(t *testing.T) {
399+
t.Parallel()
400+
401+
ctx, _ := v8go.NewContext()
402+
val, _ := ctx.RunScript("1", "")
403+
if _, err := val.AsFunction(); err == nil {
404+
t.Error("Expected error but got <nil>")
405+
}
406+
val, err := ctx.RunScript("(a, b) => { return a + b; }", "")
407+
if err != nil {
408+
t.Errorf("Unexpected error: %v", err)
409+
}
410+
if _, err := val.AsFunction(); err != nil {
411+
t.Errorf("Expected success but got: %v", err)
412+
}
413+
414+
}
415+
398416
func TestValueIsXXX(t *testing.T) {
399417
t.Parallel()
400418
iso, _ := v8go.NewIsolate()

0 commit comments

Comments
 (0)