1use std::rc::Rc;
9
10use super::{Cx, Throw};
11use crate::builtins::Native;
12use crate::embed::NativeCx;
13use crate::heap::object::InternalData;
14use crate::value::Value;
15
16#[derive(Clone, Copy, Debug)]
21pub(crate) struct CallSite {
22 pub this: usize,
23 pub args: usize,
24 pub argc: usize,
25 pub construct: bool,
26 pub function: Value,
27 pub new_target: Value,
28}
29
30impl CallSite {
31 #[inline]
32 pub fn this(&self, cx: &Cx<'_>) -> Value {
33 cx.heap.vm.registers[self.this]
34 }
35
36 #[inline]
37 pub fn arg(&self, cx: &Cx<'_>, index: usize) -> Value {
38 if index < self.argc {
39 cx.heap.vm.registers[self.args + index]
40 } else {
41 Value::UNDEFINED
42 }
43 }
44
45 #[inline]
47 pub fn args<'a>(&self, cx: &'a Cx<'_>) -> &'a [Value] {
48 &cx.heap.vm.registers[self.args..self.args + self.argc]
49 }
50
51 pub fn arg_list(&self, cx: &Cx<'_>) -> Vec<Value> {
53 self.args(cx).to_vec()
54 }
55
56 pub fn data(&self, cx: &Cx<'_>, slot: usize) -> Value {
58 let function = self.function.as_object().expect("a native closure");
59 let data = cx
60 .heap
61 .function_scope(function)
62 .expect("a native closure's data");
63 cx.heap.internal_slot(data.object(), slot)
64 }
65
66 pub fn set_data(&self, cx: &mut Cx<'_>, slot: usize, value: Value) {
68 let function = self.function.as_object().expect("a native closure");
69 let data = cx
70 .heap
71 .function_scope(function)
72 .expect("a native closure's data");
73 cx.heap.set_internal_slot(data.object(), slot, value);
74 }
75}
76
77#[inline(never)]
80pub(crate) fn call(cx: &mut Cx<'_>, native: Native, site: CallSite) -> Result<Value, Throw> {
81 let mark = cx.heap.roots().mark();
82 let result = (native.function())(cx, site);
83 cx.heap.roots().truncate(mark);
84 result
85}
86
87pub type HostFn = dyn Fn(&mut NativeCx<'_>, NativeCall) -> Result<Value, Throw>;
90
91#[derive(Clone)]
93pub struct HostBody(pub Rc<HostFn>);
94
95impl std::fmt::Debug for HostBody {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.write_str("HostBody")
98 }
99}
100
101#[derive(Clone, Copy, Debug)]
105pub struct NativeCall(CallSite);
106
107impl NativeCall {
108 pub fn this(&self, cx: &NativeCx<'_>) -> Value {
109 cx.heap.vm.registers[self.0.this]
110 }
111
112 pub fn arg(&self, cx: &NativeCx<'_>, index: usize) -> Value {
114 if index < self.0.argc {
115 cx.heap.vm.registers[self.0.args + index]
116 } else {
117 Value::UNDEFINED
118 }
119 }
120
121 pub fn argc(&self) -> usize {
122 self.0.argc
123 }
124
125 pub fn args<'a>(&self, cx: &'a NativeCx<'_>) -> &'a [Value] {
126 &cx.heap.vm.registers[self.0.args..self.0.args + self.0.argc]
127 }
128
129 pub fn new_target(&self) -> Value {
131 self.0.new_target
132 }
133
134 pub fn is_construct(&self) -> bool {
135 self.0.construct
136 }
137
138 pub fn callee(&self) -> Value {
140 self.0.function
141 }
142
143 pub fn data(&self, cx: &NativeCx<'_>, slot: usize) -> Value {
145 cx.heap.internal_slot(self.record(cx).object(), slot)
146 }
147
148 pub fn set_data(&self, cx: &mut NativeCx<'_>, slot: usize, value: Value) {
149 let record = self.record(cx);
150 cx.heap.set_internal_slot(record.object(), slot, value);
151 }
152
153 pub fn data_len(&self, cx: &NativeCx<'_>) -> usize {
155 cx.heap.internal(self.record(cx).object()).slots.len()
156 }
157
158 fn record(&self, cx: &NativeCx<'_>) -> crate::ScopeId {
159 let function = self.0.function.as_object().expect("a host function");
160 cx.heap
161 .function_scope(function)
162 .expect("a host closure's data")
163 }
164}
165
166pub(crate) fn host_call(cx: &mut Cx<'_>, site: CallSite) -> Result<Value, Throw> {
170 let function = site.function.as_object().expect("a host function");
171 let data = cx
172 .heap
173 .function_scope(function)
174 .expect("a host closure's data");
175 let InternalData::HostFn(body) = &cx.heap.internal(data.object()).data else {
176 unreachable!("a host function's data holds its body");
177 };
178 let body = Rc::clone(&body.0);
179 if !cx.heap.vm.enter_native() {
183 return Err(super::error::stack_overflow(cx));
184 }
185 let result = body(&mut NativeCx::from_cx(cx), NativeCall(site));
186 cx.heap.vm.leave_native();
187 result
188}