use std::rc::Rc;
use super::{Cx, Throw};
use crate::builtins::Native;
use crate::embed::NativeCx;
use crate::heap::object::InternalData;
use crate::value::Value;
#[derive(Clone, Copy, Debug)]
pub(crate) struct CallSite {
pub this: usize,
pub args: usize,
pub argc: usize,
pub construct: bool,
pub function: Value,
pub new_target: Value,
}
impl CallSite {
#[inline]
pub fn this(&self, cx: &Cx<'_>) -> Value {
cx.heap.vm.registers[self.this]
}
#[inline]
pub fn arg(&self, cx: &Cx<'_>, index: usize) -> Value {
if index < self.argc {
cx.heap.vm.registers[self.args + index]
} else {
Value::UNDEFINED
}
}
#[inline]
pub fn args<'a>(&self, cx: &'a Cx<'_>) -> &'a [Value] {
&cx.heap.vm.registers[self.args..self.args + self.argc]
}
pub fn arg_list(&self, cx: &Cx<'_>) -> Vec<Value> {
self.args(cx).to_vec()
}
pub fn data(&self, cx: &Cx<'_>, slot: usize) -> Value {
let function = self.function.as_object().expect("a native closure");
let data = cx
.heap
.function_scope(function)
.expect("a native closure's data");
cx.heap.internal_slot(data.object(), slot)
}
pub fn set_data(&self, cx: &mut Cx<'_>, slot: usize, value: Value) {
let function = self.function.as_object().expect("a native closure");
let data = cx
.heap
.function_scope(function)
.expect("a native closure's data");
cx.heap.set_internal_slot(data.object(), slot, value);
}
}
#[inline(never)]
pub(crate) fn call(cx: &mut Cx<'_>, native: Native, site: CallSite) -> Result<Value, Throw> {
let mark = cx.heap.roots().mark();
let result = (native.function())(cx, site);
cx.heap.roots().truncate(mark);
result
}
pub type HostFn = dyn Fn(&mut NativeCx<'_>, NativeCall) -> Result<Value, Throw>;
#[derive(Clone)]
pub struct HostBody(pub Rc<HostFn>);
impl std::fmt::Debug for HostBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("HostBody")
}
}
#[derive(Clone, Copy, Debug)]
pub struct NativeCall(CallSite);
impl NativeCall {
pub fn this(&self, cx: &NativeCx<'_>) -> Value {
cx.heap.vm.registers[self.0.this]
}
pub fn arg(&self, cx: &NativeCx<'_>, index: usize) -> Value {
if index < self.0.argc {
cx.heap.vm.registers[self.0.args + index]
} else {
Value::UNDEFINED
}
}
pub fn argc(&self) -> usize {
self.0.argc
}
pub fn args<'a>(&self, cx: &'a NativeCx<'_>) -> &'a [Value] {
&cx.heap.vm.registers[self.0.args..self.0.args + self.0.argc]
}
pub fn new_target(&self) -> Value {
self.0.new_target
}
pub fn is_construct(&self) -> bool {
self.0.construct
}
pub fn callee(&self) -> Value {
self.0.function
}
pub fn data(&self, cx: &NativeCx<'_>, slot: usize) -> Value {
cx.heap.internal_slot(self.record(cx).object(), slot)
}
pub fn set_data(&self, cx: &mut NativeCx<'_>, slot: usize, value: Value) {
let record = self.record(cx);
cx.heap.set_internal_slot(record.object(), slot, value);
}
pub fn data_len(&self, cx: &NativeCx<'_>) -> usize {
cx.heap.internal(self.record(cx).object()).slots.len()
}
fn record(&self, cx: &NativeCx<'_>) -> crate::ScopeId {
let function = self.0.function.as_object().expect("a host function");
cx.heap
.function_scope(function)
.expect("a host closure's data")
}
}
pub(crate) fn host_call(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
let function = site.function.as_object().expect("a host function");
let data = cx
.heap
.function_scope(function)
.expect("a host closure's data");
let InternalData::HostFn(body) = &cx.heap.internal(data.object()).data else {
unreachable!("a host function's data holds its body");
};
let body = Rc::clone(&body.0);
if !cx.heap.vm.enter_native() {
return Err(super::error::stack_overflow(cx));
}
let result = body(&mut NativeCx::from_cx(cx), NativeCall(site));
cx.heap.vm.leave_native();
result
}