rusty_v8/
function.rs

1use std::convert::TryFrom;
2use std::marker::PhantomData;
3use std::ptr::null;
4
5use crate::scope::CallbackScope;
6use crate::support::MapFnFrom;
7use crate::support::MapFnTo;
8use crate::support::ToCFn;
9use crate::support::UnitType;
10use crate::support::{int, Opaque};
11use crate::Context;
12use crate::Function;
13use crate::HandleScope;
14use crate::Local;
15use crate::Name;
16use crate::Object;
17use crate::Signature;
18use crate::String;
19use crate::Value;
20
21extern "C" {
22  fn v8__Function__New(
23    context: *const Context,
24    callback: FunctionCallback,
25    data_or_null: *const Value,
26    length: i32,
27    constructor_behavior: ConstructorBehavior,
28    side_effect_type: SideEffectType,
29  ) -> *const Function;
30  fn v8__Function__Call(
31    this: *const Function,
32    context: *const Context,
33    recv: *const Value,
34    argc: int,
35    argv: *const *const Value,
36  ) -> *const Value;
37  fn v8__Function__NewInstance(
38    this: *const Function,
39    context: *const Context,
40    argc: int,
41    argv: *const *const Value,
42  ) -> *const Object;
43  fn v8__Function__GetName(this: *const Function) -> *const String;
44  fn v8__Function__SetName(this: *const Function, name: *const String);
45
46  fn v8__FunctionCallbackInfo__GetReturnValue(
47    info: *const FunctionCallbackInfo,
48  ) -> *mut Value;
49  fn v8__FunctionCallbackInfo__This(
50    this: *const FunctionCallbackInfo,
51  ) -> *const Object;
52  fn v8__FunctionCallbackInfo__Length(this: *const FunctionCallbackInfo)
53    -> int;
54  fn v8__FunctionCallbackInfo__GetArgument(
55    this: *const FunctionCallbackInfo,
56    i: int,
57  ) -> *const Value;
58  fn v8__FunctionCallbackInfo__Data(
59    this: *const FunctionCallbackInfo,
60  ) -> *const Value;
61
62  fn v8__PropertyCallbackInfo__GetReturnValue(
63    this: *const PropertyCallbackInfo,
64  ) -> *mut Value;
65  fn v8__PropertyCallbackInfo__This(
66    this: *const PropertyCallbackInfo,
67  ) -> *const Object;
68
69  fn v8__ReturnValue__Set(this: *mut ReturnValue, value: *const Value);
70  fn v8__ReturnValue__Get(this: *const ReturnValue) -> *const Value;
71}
72
73// Ad-libbed - V8 does not document ConstructorBehavior.
74/// ConstructorBehavior::Allow creates a regular API function.
75///
76/// ConstructorBehavior::Throw creates a "concise" API function, a function
77/// without a ".prototype" property, that is somewhat faster to create and has
78/// a smaller footprint. Functionally equivalent to ConstructorBehavior::Allow
79/// followed by a call to FunctionTemplate::RemovePrototype().
80#[repr(C)]
81pub enum ConstructorBehavior {
82  Throw,
83  Allow,
84}
85
86/// Options for marking whether callbacks may trigger JS-observable side
87/// effects. Side-effect-free callbacks are allowlisted during debug evaluation
88/// with throwOnSideEffect. It applies when calling a Function,
89/// FunctionTemplate, or an Accessor callback. For Interceptors, please see
90/// PropertyHandlerFlags's kHasNoSideEffect.
91/// Callbacks that only cause side effects to the receiver are allowlisted if
92/// invoked on receiver objects that are created within the same debug-evaluate
93/// call, as these objects are temporary and the side effect does not escape.
94#[repr(C)]
95pub enum SideEffectType {
96  HasSideEffect,
97  HasNoSideEffect,
98  HasSideEffectToReceiver,
99}
100
101#[repr(C)]
102#[derive(Default)]
103pub(crate) struct CFunction([usize; 2]);
104
105// Note: the 'cb lifetime is required because the ReturnValue object must not
106// outlive the FunctionCallbackInfo/PropertyCallbackInfo object from which it
107// is derived.
108#[repr(C)]
109#[derive(Debug)]
110pub struct ReturnValue<'cb>(*mut Value, PhantomData<&'cb ()>);
111
112/// In V8 ReturnValue<> has a type parameter, but
113/// it turns out that in most of the APIs it's ReturnValue<Value>
114/// and for our purposes we currently don't need
115/// other types. So for now it's a simplified version.
116impl<'cb> ReturnValue<'cb> {
117  fn from_function_callback_info(info: *const FunctionCallbackInfo) -> Self {
118    let slot = unsafe { v8__FunctionCallbackInfo__GetReturnValue(info) };
119    Self(slot, PhantomData)
120  }
121
122  fn from_property_callback_info(info: *const PropertyCallbackInfo) -> Self {
123    let slot = unsafe { v8__PropertyCallbackInfo__GetReturnValue(info) };
124    Self(slot, PhantomData)
125  }
126
127  // NOTE: simplest setter, possibly we'll need to add
128  // more setters specialized per type
129  pub fn set(&mut self, value: Local<Value>) {
130    unsafe { v8__ReturnValue__Set(&mut *self, &*value) }
131  }
132
133  /// Getter. Creates a new Local<> so it comes with a certain performance
134  /// hit. If the ReturnValue was not yet set, this will return the undefined
135  /// value.
136  pub fn get<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Value> {
137    unsafe { scope.cast_local(|_| v8__ReturnValue__Get(self)) }.unwrap()
138  }
139}
140
141/// The argument information given to function call callbacks.  This
142/// class provides access to information about the context of the call,
143/// including the receiver, the number and values of arguments, and
144/// the holder of the function.
145#[repr(C)]
146#[derive(Debug)]
147pub struct FunctionCallbackInfo {
148  // The layout of this struct must match that of `class FunctionCallbackInfo`
149  // as defined in v8.h.
150  implicit_args: *mut Opaque,
151  values: *const Value,
152  length: int,
153}
154
155/// The information passed to a property callback about the context
156/// of the property access.
157#[repr(C)]
158#[derive(Debug)]
159pub struct PropertyCallbackInfo {
160  // The layout of this struct must match that of `class PropertyCallbackInfo`
161  // as defined in v8.h.
162  args: *mut Opaque,
163}
164
165#[derive(Debug)]
166pub struct FunctionCallbackArguments<'s> {
167  info: *const FunctionCallbackInfo,
168  phantom: PhantomData<&'s ()>,
169}
170
171impl<'s> FunctionCallbackArguments<'s> {
172  pub(crate) fn from_function_callback_info(
173    info: *const FunctionCallbackInfo,
174  ) -> Self {
175    Self {
176      info,
177      phantom: PhantomData,
178    }
179  }
180
181  /// Returns the receiver. This corresponds to the "this" value.
182  pub fn this(&self) -> Local<'s, Object> {
183    unsafe {
184      Local::from_raw(v8__FunctionCallbackInfo__This(self.info)).unwrap()
185    }
186  }
187
188  /// Returns the data argument specified when creating the callback.
189  pub fn data(&self) -> Option<Local<'s, Value>> {
190    unsafe { Local::from_raw(v8__FunctionCallbackInfo__Data(self.info)) }
191  }
192
193  /// The number of available arguments.
194  pub fn length(&self) -> int {
195    unsafe {
196      let length = (*self.info).length;
197      debug_assert_eq!(length, v8__FunctionCallbackInfo__Length(self.info));
198      length
199    }
200  }
201
202  /// Accessor for the available arguments. Returns `undefined` if the index is
203  /// out of bounds.
204  pub fn get(&self, i: int) -> Local<'s, Value> {
205    unsafe {
206      Local::from_raw(v8__FunctionCallbackInfo__GetArgument(self.info, i))
207        .unwrap()
208    }
209  }
210}
211
212#[derive(Debug)]
213pub struct PropertyCallbackArguments<'s> {
214  info: *const PropertyCallbackInfo,
215  phantom: PhantomData<&'s ()>,
216}
217
218impl<'s> PropertyCallbackArguments<'s> {
219  pub(crate) fn from_property_callback_info(
220    info: *const PropertyCallbackInfo,
221  ) -> Self {
222    Self {
223      info,
224      phantom: PhantomData,
225    }
226  }
227
228  /// Returns the receiver. In many cases, this is the object on which the
229  /// property access was intercepted. When using
230  /// `Reflect.get`, `Function.prototype.call`, or similar functions, it is the
231  /// object passed in as receiver or thisArg.
232  ///
233  /// ```c++
234  ///   void GetterCallback(Local<Name> name,
235  ///                       const v8::PropertyCallbackInfo<v8::Value>& info) {
236  ///      auto context = info.GetIsolate()->GetCurrentContext();
237  ///
238  ///      v8::Local<v8::Value> a_this =
239  ///          info.This()
240  ///              ->GetRealNamedProperty(context, v8_str("a"))
241  ///              .ToLocalChecked();
242  ///      v8::Local<v8::Value> a_holder =
243  ///          info.Holder()
244  ///              ->GetRealNamedProperty(context, v8_str("a"))
245  ///              .ToLocalChecked();
246  ///
247  ///     CHECK(v8_str("r")->Equals(context, a_this).FromJust());
248  ///     CHECK(v8_str("obj")->Equals(context, a_holder).FromJust());
249  ///
250  ///     info.GetReturnValue().Set(name);
251  ///   }
252  ///
253  ///   v8::Local<v8::FunctionTemplate> templ =
254  ///   v8::FunctionTemplate::New(isolate);
255  ///   templ->InstanceTemplate()->SetHandler(
256  ///       v8::NamedPropertyHandlerConfiguration(GetterCallback));
257  ///   LocalContext env;
258  ///   env->Global()
259  ///       ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
260  ///                                            .ToLocalChecked()
261  ///                                            ->NewInstance(env.local())
262  ///                                            .ToLocalChecked())
263  ///       .FromJust();
264  ///
265  ///   CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)");
266  /// ```
267  pub fn this(&self) -> Local<'s, Object> {
268    unsafe {
269      Local::from_raw(v8__PropertyCallbackInfo__This(self.info)).unwrap()
270    }
271  }
272}
273
274pub type FunctionCallback = extern "C" fn(*const FunctionCallbackInfo);
275
276impl<F> MapFnFrom<F> for FunctionCallback
277where
278  F: UnitType + Fn(&mut HandleScope, FunctionCallbackArguments, ReturnValue),
279{
280  fn mapping() -> Self {
281    let f = |info: *const FunctionCallbackInfo| {
282      let scope = &mut unsafe { CallbackScope::new(&*info) };
283      let args = FunctionCallbackArguments::from_function_callback_info(info);
284      let rv = ReturnValue::from_function_callback_info(info);
285      (F::get())(scope, args, rv);
286    };
287    f.to_c_fn()
288  }
289}
290
291/// AccessorNameGetterCallback is used as callback functions when getting a
292/// particular property. See Object and ObjectTemplate's method SetAccessor.
293pub type AccessorNameGetterCallback<'s> =
294  extern "C" fn(Local<'s, Name>, *const PropertyCallbackInfo);
295
296impl<F> MapFnFrom<F> for AccessorNameGetterCallback<'_>
297where
298  F: UnitType
299    + Fn(&mut HandleScope, Local<Name>, PropertyCallbackArguments, ReturnValue),
300{
301  fn mapping() -> Self {
302    let f = |key: Local<Name>, info: *const PropertyCallbackInfo| {
303      let scope = &mut unsafe { CallbackScope::new(&*info) };
304      let args = PropertyCallbackArguments::from_property_callback_info(info);
305      let rv = ReturnValue::from_property_callback_info(info);
306      (F::get())(scope, key, args, rv);
307    };
308    f.to_c_fn()
309  }
310}
311
312pub type AccessorNameSetterCallback<'s> =
313  extern "C" fn(Local<'s, Name>, Local<'s, Value>, *const PropertyCallbackInfo);
314
315impl<F> MapFnFrom<F> for AccessorNameSetterCallback<'_>
316where
317  F: UnitType
318    + Fn(&mut HandleScope, Local<Name>, Local<Value>, PropertyCallbackArguments),
319{
320  fn mapping() -> Self {
321    let f = |key: Local<Name>,
322             value: Local<Value>,
323             info: *const PropertyCallbackInfo| {
324      let scope = &mut unsafe { CallbackScope::new(&*info) };
325      let args = PropertyCallbackArguments::from_property_callback_info(info);
326      (F::get())(scope, key, value, args);
327    };
328    f.to_c_fn()
329  }
330}
331
332/// A builder to construct the properties of a Function or FunctionTemplate.
333pub struct FunctionBuilder<'s, T> {
334  pub(crate) callback: FunctionCallback,
335  pub(crate) data: Option<Local<'s, Value>>,
336  pub(crate) signature: Option<Local<'s, Signature>>,
337  pub(crate) length: i32,
338  pub(crate) constructor_behavior: ConstructorBehavior,
339  pub(crate) side_effect_type: SideEffectType,
340  phantom: PhantomData<T>,
341}
342
343impl<'s, T> FunctionBuilder<'s, T> {
344  /// Create a new FunctionBuilder.
345  pub fn new(callback: impl MapFnTo<FunctionCallback>) -> Self {
346    Self {
347      callback: callback.map_fn_to(),
348      data: None,
349      signature: None,
350      length: 0,
351      constructor_behavior: ConstructorBehavior::Allow,
352      side_effect_type: SideEffectType::HasSideEffect,
353      phantom: PhantomData,
354    }
355  }
356
357  /// Set the associated data. The default is no associated data.
358  pub fn data(mut self, data: Local<'s, Value>) -> Self {
359    self.data = Some(data);
360    self
361  }
362
363  /// Set the function length. The default is 0.
364  pub fn length(mut self, length: i32) -> Self {
365    self.length = length;
366    self
367  }
368
369  /// Set the constructor behavior. The default is ConstructorBehavior::Allow.
370  pub fn constructor_behavior(
371    mut self,
372    constructor_behavior: ConstructorBehavior,
373  ) -> Self {
374    self.constructor_behavior = constructor_behavior;
375    self
376  }
377
378  /// Set the side effect type. The default is SideEffectType::HasSideEffect.
379  pub fn side_effect_type(mut self, side_effect_type: SideEffectType) -> Self {
380    self.side_effect_type = side_effect_type;
381    self
382  }
383}
384
385impl<'s> FunctionBuilder<'s, Function> {
386  /// Create the function in the current execution context.
387  pub fn build(
388    self,
389    scope: &mut HandleScope<'s>,
390  ) -> Option<Local<'s, Function>> {
391    unsafe {
392      scope.cast_local(|sd| {
393        v8__Function__New(
394          sd.get_current_context(),
395          self.callback,
396          self.data.map_or_else(null, |p| &*p),
397          self.length,
398          self.constructor_behavior,
399          self.side_effect_type,
400        )
401      })
402    }
403  }
404}
405
406impl Function {
407  /// Create a FunctionBuilder to configure a Function.
408  /// This is the same as FunctionBuilder::<Function>::new().
409  pub fn builder<'s>(
410    callback: impl MapFnTo<FunctionCallback>,
411  ) -> FunctionBuilder<'s, Self> {
412    FunctionBuilder::new(callback)
413  }
414
415  /// Create a function in the current execution context
416  /// for a given FunctionCallback.
417  pub fn new<'s>(
418    scope: &mut HandleScope<'s>,
419    callback: impl MapFnTo<FunctionCallback>,
420  ) -> Option<Local<'s, Function>> {
421    Self::builder(callback).build(scope)
422  }
423
424  pub fn call<'s>(
425    &self,
426    scope: &mut HandleScope<'s>,
427    recv: Local<Value>,
428    args: &[Local<Value>],
429  ) -> Option<Local<'s, Value>> {
430    let args = Local::slice_into_raw(args);
431    let argc = int::try_from(args.len()).unwrap();
432    let argv = args.as_ptr();
433    unsafe {
434      scope.cast_local(|sd| {
435        v8__Function__Call(self, sd.get_current_context(), &*recv, argc, argv)
436      })
437    }
438  }
439
440  pub fn new_instance<'s>(
441    &self,
442    scope: &mut HandleScope<'s>,
443    args: &[Local<Value>],
444  ) -> Option<Local<'s, Object>> {
445    let args = Local::slice_into_raw(args);
446    let argc = int::try_from(args.len()).unwrap();
447    let argv = args.as_ptr();
448    unsafe {
449      scope.cast_local(|sd| {
450        v8__Function__NewInstance(self, sd.get_current_context(), argc, argv)
451      })
452    }
453  }
454
455  pub fn get_name<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, String> {
456    unsafe { scope.cast_local(|_| v8__Function__GetName(self)).unwrap() }
457  }
458
459  pub fn set_name(&self, name: Local<String>) {
460    unsafe { v8__Function__SetName(self, &*name) }
461  }
462}