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

Support calling a method on an object. #160

Merged
merged 9 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Access "this" from function callback
- value.SameValue(otherValue) function to compare values for sameness
- Undefined, Null functions to get these constant values for the isolate
- Support for calling a method on an object.

### Changed
- Removed error return value from Context.Isolate() which never fails
Expand Down
2 changes: 1 addition & 1 deletion function.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (fn *Function) Call(args ...Valuer) (*Value, error) {
argptr = (*C.ValuePtr)(unsafe.Pointer(&cArgs[0]))
}
fn.ctx.register()
rtn := C.FunctionCall(fn.ptr, C.int(len(args)), argptr)
rtn := C.FunctionCall(fn.ptr, fn.ctx.iso.undefined.ptr, C.int(len(args)), argptr)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A little 😬

fn.ctx.deregister()
return getValue(fn.ctx, rtn), getError(rtn)
}
Expand Down
28 changes: 28 additions & 0 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ type Object struct {
*Value
}

func (o *Object) MethodCall(methodName string, args ...Valuer) (*Value, error) {
ckey := C.CString(methodName)
defer C.free(unsafe.Pointer(ckey))

getRtn := C.ObjectGet(o.ptr, ckey)
err := getError(getRtn)
if err != nil {
return nil, err
}
fn, err := getValue(o.ctx, getRtn).AsFunction()
if err != nil {
return nil, err
}

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]))
}
fn.ctx.register()
rtn := C.FunctionCall(fn.ptr, o.ptr, C.int(len(args)), argptr)
fn.ctx.deregister()
return getValue(fn.ctx, rtn), getError(rtn)
genevieve marked this conversation as resolved.
Show resolved Hide resolved
}

// Set will set a property on the Object to a given value.
// Supports all value types, eg: Object, Array, Date, Set, Map etc
// If the value passed is a Go supported primitive (string, int32, uint32, int64, uint64, float64, big.Int)
Expand Down
32 changes: 32 additions & 0 deletions object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,38 @@ import (
"rogchap.com/v8go"
)

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

ctx, _ := v8go.NewContext()
iso := ctx.Isolate()
val, _ := ctx.RunScript(`class Obj { constructor(input) { this.input = input, this.prop = "" } print() { return this.input.toString() } }; new Obj("some val")`, "")
obj, _ := val.AsObject()
val, err := obj.MethodCall("print")
failIf(t, err)
if val.String() != "some val" {
t.Errorf("unexpected value: %q", val)
}
_, err = obj.MethodCall("prop")
if err == nil {
t.Errorf("expected an error, got none")
}

val, err = ctx.RunScript(`class Obj2 { print(str) { return str.toString() }; get fails() { throw "error" } }; new Obj2()`, "")
failIf(t, err)
obj, _ = val.AsObject()
arg, _ := v8go.NewValue(iso, "arg")
val, err = obj.MethodCall("print", arg)
failIf(t, err)
if val.String() != "arg" {
t.Errorf("unexpected value: %q", val)
}
_, err = obj.MethodCall("fails")
if err == nil {
t.Errorf("expected an error, got none")
}
}

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

Expand Down
10 changes: 7 additions & 3 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1216,14 +1216,18 @@ static void buildCallArguments(Isolate* iso,
}
}

RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr args[]) {
RtnValue FunctionCall(ValuePtr ptr, ValuePtr recv, int argc, ValuePtr args[]) {
LOCAL_VALUE(ptr)

RtnValue rtn = {nullptr, nullptr};
Local<Function> fn = Local<Function>::Cast(value);
Local<Value> argv[argc];
buildCallArguments(iso, argv, argc, args);
Local<Value> recv = Undefined(iso);
MaybeLocal<Value> result = fn->Call(local_ctx, recv, argc, argv);

m_value* recv_val = static_cast<m_value*>(recv);
Local<Value> local_recv = recv_val->ptr.Get(iso);

MaybeLocal<Value> result = fn->Call(local_ctx, local_recv, argc, argv);
if (result.IsEmpty()) {
rtn.error = ExceptionError(try_catch, iso, local_ctx);
return rtn;
Expand Down
2 changes: 1 addition & 1 deletion v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ ValuePtr PromiseThen2(ValuePtr ptr, int on_fulfilled_ref, int on_rejected_ref);
ValuePtr PromiseCatch(ValuePtr ptr, int callback_ref);
extern ValuePtr PromiseResult(ValuePtr ptr);

extern RtnValue FunctionCall(ValuePtr ptr, int argc, ValuePtr argv[]);
extern RtnValue FunctionCall(ValuePtr ptr, ValuePtr recv, int argc, ValuePtr argv[]);
RtnValue FunctionNewInstance(ValuePtr ptr, int argc, ValuePtr args[]);
ValuePtr FunctionSourceMapUrl(ValuePtr ptr);

Expand Down