use std::convert::TryFrom;
use std::marker::PhantomData;
use std::ptr::null;
use std::ptr::NonNull;
use crate::scope::CallbackScope;
use crate::script_compiler::CachedData;
use crate::support::MapFnFrom;
use crate::support::MapFnTo;
use crate::support::ToCFn;
use crate::support::UnitType;
use crate::support::{int, Opaque};
use crate::undefined;
use crate::Context;
use crate::Function;
use crate::HandleScope;
use crate::Isolate;
use crate::Local;
use crate::Name;
use crate::Object;
use crate::Signature;
use crate::String;
use crate::UniqueRef;
use crate::Value;
extern "C" {
fn v8__Function__New(
context: *const Context,
callback: FunctionCallback,
data_or_null: *const Value,
length: i32,
constructor_behavior: ConstructorBehavior,
side_effect_type: SideEffectType,
) -> *const Function;
fn v8__Function__Call(
this: *const Function,
context: *const Context,
recv: *const Value,
argc: int,
argv: *const *const Value,
) -> *const Value;
fn v8__Function__NewInstance(
this: *const Function,
context: *const Context,
argc: int,
argv: *const *const Value,
) -> *const Object;
fn v8__Function__GetName(this: *const Function) -> *const String;
fn v8__Function__SetName(this: *const Function, name: *const String);
fn v8__Function__GetScriptColumnNumber(this: *const Function) -> int;
fn v8__Function__GetScriptLineNumber(this: *const Function) -> int;
fn v8__Function__CreateCodeCache(
script: *const Function,
) -> *mut CachedData<'static>;
static v8__FunctionCallbackInfo__kArgsLength: int;
static v8__PropertyCallbackInfo__kArgsLength: int;
fn v8__PropertyCallbackInfo__ShouldThrowOnError(
this: *const PropertyCallbackInfo,
) -> bool;
fn v8__ReturnValue__Set(this: *mut ReturnValue, value: *const Value);
fn v8__ReturnValue__Set__Bool(this: *mut ReturnValue, value: bool);
fn v8__ReturnValue__Set__Int32(this: *mut ReturnValue, value: i32);
fn v8__ReturnValue__Set__Uint32(this: *mut ReturnValue, value: u32);
fn v8__ReturnValue__Set__Double(this: *mut ReturnValue, value: f64);
fn v8__ReturnValue__SetNull(this: *mut ReturnValue);
fn v8__ReturnValue__SetUndefined(this: *mut ReturnValue);
fn v8__ReturnValue__SetEmptyString(this: *mut ReturnValue);
fn v8__ReturnValue__Get(this: *const ReturnValue) -> *const Value;
}
#[repr(C)]
pub enum ConstructorBehavior {
Throw,
Allow,
}
#[repr(C)]
pub enum SideEffectType {
HasSideEffect,
HasNoSideEffect,
HasSideEffectToReceiver,
}
#[repr(C)]
#[derive(Debug)]
pub struct ReturnValue<'cb>(NonNull<Value>, PhantomData<&'cb ()>);
impl<'cb> ReturnValue<'cb> {
#[inline(always)]
pub fn from_function_callback_info(info: &'cb FunctionCallbackInfo) -> Self {
let nn = info.get_return_value_non_null();
Self(nn, PhantomData)
}
#[inline(always)]
fn from_property_callback_info(info: &'cb PropertyCallbackInfo) -> Self {
let nn = info.get_return_value_non_null();
Self(nn, PhantomData)
}
#[inline(always)]
pub fn set(&mut self, value: Local<Value>) {
unsafe { v8__ReturnValue__Set(&mut *self, &*value) }
}
#[inline(always)]
pub fn set_bool(&mut self, value: bool) {
unsafe { v8__ReturnValue__Set__Bool(&mut *self, value) }
}
#[inline(always)]
pub fn set_int32(&mut self, value: i32) {
unsafe { v8__ReturnValue__Set__Int32(&mut *self, value) }
}
#[inline(always)]
pub fn set_uint32(&mut self, value: u32) {
unsafe { v8__ReturnValue__Set__Uint32(&mut *self, value) }
}
#[inline(always)]
pub fn set_double(&mut self, value: f64) {
unsafe { v8__ReturnValue__Set__Double(&mut *self, value) }
}
#[inline(always)]
pub fn set_null(&mut self) {
unsafe { v8__ReturnValue__SetNull(&mut *self) }
}
#[inline(always)]
pub fn set_undefined(&mut self) {
unsafe { v8__ReturnValue__SetUndefined(&mut *self) }
}
#[inline(always)]
pub fn set_empty_string(&mut self) {
unsafe { v8__ReturnValue__SetEmptyString(&mut *self) }
}
#[inline(always)]
pub fn get<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Value> {
unsafe { scope.cast_local(|_| v8__ReturnValue__Get(self)) }.unwrap()
}
}
#[repr(C)]
#[derive(Debug)]
pub struct FunctionCallbackInfo {
implicit_args: *mut *const Opaque,
values: *mut *const Opaque,
length: int,
}
#[allow(dead_code, non_upper_case_globals)]
impl FunctionCallbackInfo {
const kHolderIndex: i32 = 0;
const kIsolateIndex: i32 = 1;
const kReturnValueDefaultValueIndex: i32 = 2;
const kReturnValueIndex: i32 = 3;
const kDataIndex: i32 = 4;
const kNewTargetIndex: i32 = 5;
const kArgsLength: i32 = 6;
}
impl FunctionCallbackInfo {
#[inline(always)]
pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate {
let arg_nn =
self.get_implicit_arg_non_null::<*mut Isolate>(Self::kIsolateIndex);
*unsafe { arg_nn.as_ref() }
}
#[inline(always)]
pub(crate) fn get_return_value_non_null(&self) -> NonNull<Value> {
self.get_implicit_arg_non_null::<Value>(Self::kReturnValueIndex)
}
#[inline(always)]
pub(crate) fn holder(&self) -> Local<Object> {
unsafe { self.get_implicit_arg_local(Self::kHolderIndex) }
}
#[inline(always)]
pub(crate) fn new_target(&self) -> Local<Value> {
unsafe { self.get_implicit_arg_local(Self::kNewTargetIndex) }
}
#[inline(always)]
pub(crate) fn this(&self) -> Local<Object> {
unsafe { self.get_arg_local(-1) }
}
#[inline(always)]
pub(crate) fn data(&self) -> Local<Value> {
unsafe { self.get_implicit_arg_local(Self::kDataIndex) }
}
#[inline(always)]
pub(crate) fn length(&self) -> i32 {
self.length
}
#[inline(always)]
pub(crate) fn get(&self, index: int) -> Local<Value> {
if index >= 0 && index < self.length {
unsafe { self.get_arg_local(index) }
} else {
let isolate = unsafe { &mut *self.get_isolate_ptr() };
undefined(isolate).into()
}
}
#[inline(always)]
fn get_implicit_arg_non_null<T>(&self, index: i32) -> NonNull<T> {
debug_assert_eq!(
unsafe { v8__FunctionCallbackInfo__kArgsLength },
Self::kArgsLength
);
assert!(index >= 0);
assert!(index < Self::kArgsLength);
let ptr = unsafe { self.implicit_args.offset(index as isize) as *mut T };
debug_assert!(!ptr.is_null());
unsafe { NonNull::new_unchecked(ptr) }
}
#[inline(always)]
unsafe fn get_implicit_arg_local<T>(&self, index: i32) -> Local<T> {
let nn = self.get_implicit_arg_non_null::<T>(index);
Local::from_non_null(nn)
}
#[inline(always)]
unsafe fn get_arg_local<T>(&self, index: i32) -> Local<T> {
let ptr = self.values.offset(index as _) as *mut T;
debug_assert!(!ptr.is_null());
let nn = NonNull::new_unchecked(ptr);
Local::from_non_null(nn)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct PropertyCallbackInfo {
args: *mut *const Opaque,
}
#[allow(dead_code, non_upper_case_globals)]
impl PropertyCallbackInfo {
const kShouldThrowOnErrorIndex: i32 = 0;
const kHolderIndex: i32 = 1;
const kIsolateIndex: i32 = 2;
const kReturnValueDefaultValueIndex: i32 = 3;
const kReturnValueIndex: i32 = 4;
const kDataIndex: i32 = 5;
const kThisIndex: i32 = 6;
const kArgsLength: i32 = 7;
}
impl PropertyCallbackInfo {
#[inline(always)]
pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate {
let arg_nn = self.get_arg_non_null::<*mut Isolate>(Self::kIsolateIndex);
*unsafe { arg_nn.as_ref() }
}
#[inline(always)]
pub(crate) fn get_return_value_non_null(&self) -> NonNull<Value> {
self.get_arg_non_null::<Value>(Self::kReturnValueIndex)
}
#[inline(always)]
pub(crate) fn holder(&self) -> Local<Object> {
unsafe { self.get_arg_local(Self::kHolderIndex) }
}
#[inline(always)]
pub(crate) fn this(&self) -> Local<Object> {
unsafe { self.get_arg_local(Self::kThisIndex) }
}
#[inline(always)]
pub(crate) fn data(&self) -> Local<Value> {
unsafe { self.get_arg_local(Self::kDataIndex) }
}
#[inline(always)]
pub(crate) fn should_throw_on_error(&self) -> bool {
unsafe { v8__PropertyCallbackInfo__ShouldThrowOnError(self) }
}
#[inline(always)]
fn get_arg_non_null<T>(&self, index: i32) -> NonNull<T> {
debug_assert_eq!(
unsafe { v8__PropertyCallbackInfo__kArgsLength },
Self::kArgsLength
);
assert!(index >= 0);
assert!(index < Self::kArgsLength);
let ptr = unsafe { self.args.offset(index as isize) as *mut T };
debug_assert!(!ptr.is_null());
unsafe { NonNull::new_unchecked(ptr) }
}
#[inline(always)]
unsafe fn get_arg_local<T>(&self, index: i32) -> Local<T> {
let nn = self.get_arg_non_null::<T>(index);
Local::from_non_null(nn)
}
}
#[derive(Debug)]
pub struct FunctionCallbackArguments<'s>(&'s FunctionCallbackInfo);
impl<'s> FunctionCallbackArguments<'s> {
#[inline(always)]
pub fn from_function_callback_info(info: &'s FunctionCallbackInfo) -> Self {
Self(info)
}
#[inline(always)]
pub unsafe fn get_isolate(&mut self) -> &mut Isolate {
&mut *self.0.get_isolate_ptr()
}
#[inline(always)]
pub fn holder(&self) -> Local<'s, Object> {
self.0.holder()
}
#[inline(always)]
pub fn new_target(&self) -> Local<'s, Value> {
self.0.new_target()
}
#[inline(always)]
pub fn this(&self) -> Local<'s, Object> {
self.0.this()
}
#[inline(always)]
pub fn data(&self) -> Local<'s, Value> {
self.0.data()
}
#[inline(always)]
pub fn length(&self) -> int {
self.0.length()
}
#[inline(always)]
pub fn get(&self, i: int) -> Local<'s, Value> {
self.0.get(i)
}
}
#[derive(Debug)]
pub struct PropertyCallbackArguments<'s>(&'s PropertyCallbackInfo);
impl<'s> PropertyCallbackArguments<'s> {
#[inline(always)]
pub(crate) fn from_property_callback_info(
info: &'s PropertyCallbackInfo,
) -> Self {
Self(info)
}
#[inline(always)]
pub fn holder(&self) -> Local<'s, Object> {
self.0.holder()
}
#[inline(always)]
pub fn this(&self) -> Local<'s, Object> {
self.0.this()
}
#[inline(always)]
pub fn data(&self) -> Local<'s, Value> {
self.0.data()
}
#[inline(always)]
pub fn should_throw_on_error(&self) -> bool {
self.0.should_throw_on_error()
}
}
pub type FunctionCallback = extern "C" fn(*const FunctionCallbackInfo);
impl<'a, F> MapFnFrom<F> for FunctionCallback
where
F: UnitType
+ Fn(&mut HandleScope<'a>, FunctionCallbackArguments<'a>, ReturnValue),
{
fn mapping() -> Self {
let f = |info: *const FunctionCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = FunctionCallbackArguments::from_function_callback_info(info);
let rv = ReturnValue::from_function_callback_info(info);
(F::get())(scope, args, rv);
};
f.to_c_fn()
}
}
pub type AccessorNameGetterCallback<'s> =
extern "C" fn(Local<'s, Name>, *const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for AccessorNameGetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, Local<Name>, PropertyCallbackArguments, ReturnValue),
{
fn mapping() -> Self {
let f = |key: Local<Name>, info: *const PropertyCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
let rv = ReturnValue::from_property_callback_info(info);
(F::get())(scope, key, args, rv);
};
f.to_c_fn()
}
}
pub type AccessorNameSetterCallback<'s> =
extern "C" fn(Local<'s, Name>, Local<'s, Value>, *const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for AccessorNameSetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, Local<Name>, Local<Value>, PropertyCallbackArguments),
{
fn mapping() -> Self {
let f = |key: Local<Name>,
value: Local<Value>,
info: *const PropertyCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
(F::get())(scope, key, value, args);
};
f.to_c_fn()
}
}
pub type PropertyEnumeratorCallback<'s> =
extern "C" fn(*const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for PropertyEnumeratorCallback<'_>
where
F: UnitType + Fn(&mut HandleScope, PropertyCallbackArguments, ReturnValue),
{
fn mapping() -> Self {
let f = |info: *const PropertyCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
let rv = ReturnValue::from_property_callback_info(info);
(F::get())(scope, args, rv);
};
f.to_c_fn()
}
}
pub type IndexedPropertyGetterCallback<'s> =
extern "C" fn(u32, *const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for IndexedPropertyGetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, u32, PropertyCallbackArguments, ReturnValue),
{
fn mapping() -> Self {
let f = |index: u32, info: *const PropertyCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
let rv = ReturnValue::from_property_callback_info(info);
(F::get())(scope, index, args, rv);
};
f.to_c_fn()
}
}
pub type IndexedPropertySetterCallback<'s> =
extern "C" fn(u32, Local<'s, Value>, *const PropertyCallbackInfo);
impl<F> MapFnFrom<F> for IndexedPropertySetterCallback<'_>
where
F: UnitType
+ Fn(&mut HandleScope, u32, Local<Value>, PropertyCallbackArguments),
{
fn mapping() -> Self {
let f =
|index: u32, value: Local<Value>, info: *const PropertyCallbackInfo| {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let args = PropertyCallbackArguments::from_property_callback_info(info);
(F::get())(scope, index, value, args);
};
f.to_c_fn()
}
}
pub struct FunctionBuilder<'s, T> {
pub(crate) callback: FunctionCallback,
pub(crate) data: Option<Local<'s, Value>>,
pub(crate) signature: Option<Local<'s, Signature>>,
pub(crate) length: i32,
pub(crate) constructor_behavior: ConstructorBehavior,
pub(crate) side_effect_type: SideEffectType,
phantom: PhantomData<T>,
}
impl<'s, T> FunctionBuilder<'s, T> {
#[inline(always)]
pub fn new(callback: impl MapFnTo<FunctionCallback>) -> Self {
Self::new_raw(callback.map_fn_to())
}
#[inline(always)]
pub fn new_raw(callback: FunctionCallback) -> Self {
Self {
callback,
data: None,
signature: None,
length: 0,
constructor_behavior: ConstructorBehavior::Allow,
side_effect_type: SideEffectType::HasSideEffect,
phantom: PhantomData,
}
}
#[inline(always)]
pub fn data(mut self, data: Local<'s, Value>) -> Self {
self.data = Some(data);
self
}
#[inline(always)]
pub fn length(mut self, length: i32) -> Self {
self.length = length;
self
}
#[inline(always)]
pub fn constructor_behavior(
mut self,
constructor_behavior: ConstructorBehavior,
) -> Self {
self.constructor_behavior = constructor_behavior;
self
}
#[inline(always)]
pub fn side_effect_type(mut self, side_effect_type: SideEffectType) -> Self {
self.side_effect_type = side_effect_type;
self
}
}
impl<'s> FunctionBuilder<'s, Function> {
#[inline(always)]
pub fn build(
self,
scope: &mut HandleScope<'s>,
) -> Option<Local<'s, Function>> {
unsafe {
scope.cast_local(|sd| {
v8__Function__New(
sd.get_current_context(),
self.callback,
self.data.map_or_else(null, |p| &*p),
self.length,
self.constructor_behavior,
self.side_effect_type,
)
})
}
}
}
impl Function {
#[inline(always)]
pub fn builder<'s>(
callback: impl MapFnTo<FunctionCallback>,
) -> FunctionBuilder<'s, Self> {
FunctionBuilder::new(callback)
}
#[inline(always)]
pub fn builder_raw<'s>(
callback: FunctionCallback,
) -> FunctionBuilder<'s, Self> {
FunctionBuilder::new_raw(callback)
}
#[inline(always)]
pub fn new<'s>(
scope: &mut HandleScope<'s>,
callback: impl MapFnTo<FunctionCallback>,
) -> Option<Local<'s, Function>> {
Self::builder(callback).build(scope)
}
#[inline(always)]
pub fn new_raw<'s>(
scope: &mut HandleScope<'s>,
callback: FunctionCallback,
) -> Option<Local<'s, Function>> {
Self::builder_raw(callback).build(scope)
}
#[inline(always)]
pub fn call<'s>(
&self,
scope: &mut HandleScope<'s>,
recv: Local<Value>,
args: &[Local<Value>],
) -> Option<Local<'s, Value>> {
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__Function__Call(self, sd.get_current_context(), &*recv, argc, argv)
})
}
}
#[inline(always)]
pub fn new_instance<'s>(
&self,
scope: &mut HandleScope<'s>,
args: &[Local<Value>],
) -> Option<Local<'s, Object>> {
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__Function__NewInstance(self, sd.get_current_context(), argc, argv)
})
}
}
#[inline(always)]
pub fn get_name<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, String> {
unsafe { scope.cast_local(|_| v8__Function__GetName(self)).unwrap() }
}
#[inline(always)]
pub fn set_name(&self, name: Local<String>) {
unsafe { v8__Function__SetName(self, &*name) }
}
#[inline(always)]
pub fn get_script_column_number(&self) -> Option<u32> {
let ret = unsafe { v8__Function__GetScriptColumnNumber(self) };
(ret >= 0).then_some(ret as u32)
}
#[inline(always)]
pub fn get_script_line_number(&self) -> Option<u32> {
let ret = unsafe { v8__Function__GetScriptLineNumber(self) };
(ret >= 0).then_some(ret as u32)
}
#[inline(always)]
pub fn create_code_cache(&self) -> Option<UniqueRef<CachedData<'static>>> {
let code_cache =
unsafe { UniqueRef::try_from_raw(v8__Function__CreateCodeCache(self)) };
#[cfg(debug_assertions)]
if let Some(code_cache) = &code_cache {
debug_assert_eq!(
code_cache.buffer_policy(),
crate::script_compiler::BufferPolicy::BufferOwned
);
}
code_cache
}
}