From 6341a3be320f10fea0beb828730adcafa2001cab Mon Sep 17 00:00:00 2001 From: Roger Chapman Date: Fri, 9 Apr 2021 14:57:47 +1000 Subject: [PATCH 1/2] only run FOSSA on master --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 41414be5..e919471c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,7 @@ jobs: - name: Add GOPATH to GITHUB_ENV run: echo "GOPATH=$(go env GOPATH)" >>"$GITHUB_ENV" - name: Scan and upload FOSSA data + if: github.ref == 'refs/heads/master' run: | curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash fossa analyze From 25d7afaf8b989aa2f83391c2e45f537751b585c8 Mon Sep 17 00:00:00 2001 From: Rob Figueiredo Date: Fri, 9 Apr 2021 01:00:53 -0400 Subject: [PATCH 2/2] 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 --- CHANGELOG.md | 1 + function.go | 30 +++++++++++++++++++++++ function_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ v8go.cc | 25 +++++++++++++++++++ v8go.h | 2 ++ value.go | 7 ++++++ value_test.go | 18 ++++++++++++++ 7 files changed, 146 insertions(+) create mode 100644 function.go create mode 100644 function_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7c711d..46780044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/function.go b/function.go new file mode 100644 index 00000000..ef86b88a --- /dev/null +++ b/function.go @@ -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) +} diff --git a/function_test.go b/function_test.go new file mode 100644 index 00000000..e8a59dcb --- /dev/null +++ b/function_test.go @@ -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) + } +} diff --git a/v8go.cc b/v8go.cc index 1a44c3df..f821c83f 100644 --- a/v8go.cc +++ b/v8go.cc @@ -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 fn = Local::Cast(value); + Local argv[argc]; + for (int i = 0; i < argc; i++) { + m_value* arg = static_cast(args[i]); + argv[i] = arg->ptr.Get(iso); + } + Local recv = Undefined(iso); + MaybeLocal 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>(iso, result.ToLocalChecked()); + rtn.value = tracked_value(ctx, rtnval); + return rtn; +} + /******** Exceptions *********/ ValuePtr ExceptionError(IsolatePtr iso_ptr, const char* message) { diff --git a/v8go.h b/v8go.h index 68fb52fe..f7f2df98 100644 --- a/v8go.h +++ b/v8go.h @@ -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, diff --git a/value.go b/value.go index ed4d8208..efa81916 100644 --- a/value.go +++ b/value.go @@ -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) diff --git a/value_test.go b/value_test.go index 74a82eb7..a72daefc 100644 --- a/value_test.go +++ b/value_test.go @@ -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 ") + } + 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()