diff --git a/include/v8-function.h b/include/v8-function.h index 9424a86fda..2e12ed689b 100644 --- a/include/v8-function.h +++ b/include/v8-function.h @@ -51,6 +51,13 @@ class V8_EXPORT Function : public Object { Local context, int argc, Local argv[], SideEffectType side_effect_type = SideEffectType::kHasSideEffect) const; + /** + * Returns a bound function. Roughly equivalent to JS's function.prototype.bind, + * where that and/or bound_args must be specified over variadic (this, ...args) + */ + V8_WARN_UNUSED_RESULT MaybeLocal Bind( + Local that, Local bound_args); + V8_WARN_UNUSED_RESULT MaybeLocal Call(Local context, Local recv, int argc, Local argv[]); diff --git a/src/api/api.cc b/src/api/api.cc index 2f5f4192c3..60363b31c9 100644 --- a/src/api/api.cc +++ b/src/api/api.cc @@ -5143,6 +5143,48 @@ MaybeLocal Function::NewInstanceWithSideEffectType( RETURN_ESCAPED(result); } +MaybeLocal Function::Bind(v8::Local that, + v8::Local bound_args) { + i::Handle self; + i::Handle that_obj; + v8::Isolate* isolate = this->GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast(isolate); + + if (that.IsEmpty()) { + Utils::ApiCheck(!bound_args.IsEmpty(), + "Function::Bind", + "A that argument and/or an array of argument values must be specified"); + + // xref: `TorqueGeneratedJSBoundFunction::set_bound_this` (+1 line) + // `bound_this.IsNullOrUndefined` + // specifying undefined is acceptable; replace an empty "that" local with undefined + that_obj = Utils::OpenHandle(*v8::Undefined(isolate)); + } else { + that_obj = Utils::OpenHandle(*that); + } + + self = Utils::OpenHandle(this); + + // derive a base vector of internal object handles by unwrapping an optional v8::Array + base::ScopedVector> argv(bound_args.IsEmpty() ? 0 : bound_args->Length()); + if (!bound_args.IsEmpty()) { + for (int i = 0; i < bound_args->Length(); ++i) { + MaybeLocal arg = bound_args->Get(isolate->GetCurrentContext(), i); + CHECK(!arg.IsEmpty()); + argv[i] = Utils::OpenHandle(*arg.ToLocalChecked()); + } + } + + i::MaybeHandle bound_function = + i_isolate->factory()->NewJSBoundFunction(self, that_obj, argv); + + if (bound_function.is_null()) { + return {}; + } + + return ToApiHandle(bound_function.ToHandleChecked()); +} + MaybeLocal Function::Call(Local context, v8::Local recv, int argc, v8::Local argv[]) {