diff --git a/src/binding.cc b/src/binding.cc index 3134d9e5b4..0042449070 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1440,6 +1440,13 @@ void v8__ObjectTemplate__SetImmutableProto(const v8::ObjectTemplate& self) { return ptr_to_local(&self)->SetImmutableProto(); } +void v8__ObjectTemplate__SetCallAsFunctionHandler( + const v8::ObjectTemplate& self, v8::FunctionCallback callback, + const v8::Value* data_or_null) { + ptr_to_local(&self)->SetCallAsFunctionHandler(callback, + ptr_to_local(data_or_null)); +} + const v8::Object* v8__Object__New(v8::Isolate* isolate) { return local_to_ptr(v8::Object::New(isolate)); } @@ -1743,6 +1750,31 @@ const v8::Array* v8__Object__PreviewEntries(const v8::Object& self, return maybe_local_to_ptr(ptr_to_local(&self)->PreviewEntries(is_key_value)); } +const bool v8__Object__IsCallable(const v8::Object& self) { + return ptr_to_local(&self)->IsCallable(); +} + +const bool v8__Object__IsConstructor(const v8::Object& self) { + return ptr_to_local(&self)->IsConstructor(); +} + +const v8::Value* v8__Object__CallAsFunction(const v8::Object& self, + const v8::Context& context, + const v8::Value& recv, int argc, + const v8::Value* const argv[]) { + return maybe_local_to_ptr(ptr_to_local(&self)->CallAsFunction( + ptr_to_local(&context), ptr_to_local(&recv), argc, + const_ptr_array_to_local_array(argv))); +} + +const v8::Value* v8__Object__CallAsConstructor(const v8::Object& self, + const v8::Context& context, + int argc, + const v8::Value* const argv[]) { + return maybe_local_to_ptr(ptr_to_local(&self)->CallAsConstructor( + ptr_to_local(&context), argc, const_ptr_array_to_local_array(argv))); +} + const v8::Array* v8__Array__New(v8::Isolate* isolate, int length) { return local_to_ptr(v8::Array::New(isolate, length)); } diff --git a/src/object.rs b/src/object.rs index d20c968c88..7dac3c17dd 100644 --- a/src/object.rs +++ b/src/object.rs @@ -244,6 +244,21 @@ unsafe extern "C" { tag: u16, ) -> *mut RustObj; fn v8__Object__IsApiWrapper(this: *const Object) -> bool; + fn v8__Object__IsCallable(this: *const Object) -> bool; + fn v8___Object__IsConstructor(this: *const Object) -> bool; + fn v8__Object__CallAsFunction( + this: *const Object, + context: *const Context, + recv: *const Value, + argc: int, + argv: *const *const Value, + ) -> *const Value; + fn v8__Object__CallAsConstructor( + this: *const Object, + context: *const Context, + argc: int, + argv: *const *const Value, + ) -> *const Value; fn v8__Array__New(isolate: *mut RealIsolate, length: int) -> *const Array; fn v8__Array__New_with_elements( @@ -1031,6 +1046,138 @@ impl Object { } out.into() } + + /// When this Object is callable, this method returns `true`. + /// + /// Certain exotic objects may be callable despite not being + /// [`Function`](crate::Function)s, such as [`Proxy`](crate::Proxy), or other + /// objects with a `[[Call]]` internal method. Callable objects can be created + /// via + /// [`ObjectTemplate::set_call_as_function_handler`](crate::ObjectTemplate::set_call_as_function_handler). + #[must_use = "this is a pure method"] + #[inline(always)] + pub fn is_callable(&self) -> bool { + unsafe { v8__Object__IsCallable(self) } + } + + /// When this Object can be called as a constructor, this method returns + /// `true`. + #[must_use = "this is a pure method"] + #[inline(always)] + pub fn is_constructor(&self) -> bool { + unsafe { v8___Object__IsConstructor(self) } + } + + /// Calls the [`Object`] as a function with the provided `this` argument and + /// arguments. + /// + /// For more information, see [`Object::is_callable()`]. + #[inline] + pub fn call_as_function<'s>( + &self, + scope: &PinScope<'s, '_>, + recv: Local<'_, Value>, + args: &[Local<'_, Value>], + ) -> Option> { + let args = Local::slice_into_raw(args); + let argc = int::try_from(args.len()).unwrap(); + let argv = args.as_ptr(); + unsafe { + scope.cast_local(|sd| { + v8__Object__CallAsFunction( + self, + sd.get_current_context(), + &*recv, + argc, + argv, + ) + }) + } + } + + /// Calls the [`Object`] as a function in a given context. + /// + /// For more information, see [`Object::is_callable()`]. + #[inline] + pub fn call_as_function_with_context<'s>( + &self, + scope: &PinScope<'s, '_, ()>, + context: Local<'_, Context>, + recv: Local<'_, Value>, + args: &[Local<'_, Value>], + ) -> Option> { + let args = Local::slice_into_raw(args); + let argc = int::try_from(args.len()).unwrap(); + let argv = args.as_ptr(); + unsafe { + let ret = v8__Object__CallAsFunction( + self, + context.as_non_null().as_ptr(), + &*recv, + argc, + argv, + ); + if ret.is_null() { + None + } else { + scope.cast_local(|_| ret) + } + } + } + + /// Calls the [`Object`] as a constructor with the provided arguments. + /// This is similar to + /// [`Function::new_instance()`](crate::Function::new_instance). + /// + /// For more information, see [`Object::is_constructor()`]. + #[inline] + pub fn call_as_constructor<'s>( + &self, + scope: &PinScope<'s, '_>, + args: &[Local<'_, Value>], + ) -> Option> { + let args = Local::slice_into_raw(args); + let argc = int::try_from(args.len()).unwrap(); + let argv = args.as_ptr(); + unsafe { + scope.cast_local(|sd| { + v8__Object__CallAsConstructor( + self, + sd.get_current_context(), + argc, + argv, + ) + }) + } + } + + /// Calls the [`Object`] as a constructor in a given context. + /// + /// For more information, see [`Object::is_constructor()`]. + #[inline] + pub fn call_as_constructor_with_context<'s>( + &self, + scope: &PinScope<'s, '_, ()>, + context: Local<'_, Context>, + args: &[Local<'_, Value>], + ) -> Option> { + let args = Local::slice_into_raw(args); + let argc = int::try_from(args.len()).unwrap(); + let argv = args.as_ptr(); + unsafe { + let ret = v8__Object__CallAsConstructor( + self, + context.as_non_null().as_ptr(), + argc, + argv, + ); + if ret.is_null() { + None + } else { + scope.cast_local(|_| ret) + } + } + } } /// Object integrity levels can be used to restrict what can be done to an diff --git a/src/template.rs b/src/template.rs index c9248f9103..dcf7ebb753 100644 --- a/src/template.rs +++ b/src/template.rs @@ -144,6 +144,12 @@ unsafe extern "C" { ); fn v8__ObjectTemplate__SetImmutableProto(this: *const ObjectTemplate); + + fn v8__ObjecTemplate__SetCallAsFunctionHandler( + this: *const ObjectTemplate, + callback: FunctionCallback, + data_or_null: *const Value, + ); } /// Interceptor callbacks use this value to indicate whether the request was @@ -1016,4 +1022,24 @@ impl ObjectTemplate { pub fn set_immutable_proto(&self) { unsafe { v8__ObjectTemplate__SetImmutableProto(self) }; } + + /// Sets the callback to be used when calling instances created from this + /// template as a function. + /// + /// If no callback is set, instances behave like normal [`Object`]s that + /// cannot be called like [`Function`]s. + #[inline(always)] + pub fn set_call_as_function_handler( + &self, + callback: impl MapFnTo, + data: Option>, + ) { + unsafe { + v8__ObjecTemplate__SetCallAsFunctionHandler( + self, + callback.map_fn_to(), + data.map_or_else(null, |p| &*p), + ); + } + } }