1use std::{any::Any, error::Error, fmt};
4
5use sim_kernel::{
6 Args, Callable, ClassRef, Cx, Object, ObjectCompat, Result as KernelResult, ShapeRef, Value,
7};
8use sim_lib_binding::BindingCell;
9use sim_lib_mutation::{
10 EdgeId, EdgeVisitor, ManagedHandle, ManagedId, ManagedNode, ManagedObject,
11 StrongEdgeMutationError,
12};
13
14use crate::{BoundCall, CallInput, FunctionPlan, bind};
15
16pub trait FunctionBodyPolicy: Send + Sync + 'static {
22 fn invoke(
24 &self,
25 cx: &mut Cx,
26 plan: &FunctionPlan,
27 captures: &[CapturedBinding],
28 call: BoundCall,
29 ) -> KernelResult<Value>;
30}
31
32#[derive(Clone, Debug)]
34pub struct CapturedBinding {
35 cell: BindingCell,
36 managed: ManagedHandle,
37}
38
39impl CapturedBinding {
40 pub const fn new(cell: BindingCell, managed: ManagedHandle) -> Self {
42 Self { cell, managed }
43 }
44
45 pub const fn cell(&self) -> &BindingCell {
47 &self.cell
48 }
49
50 pub const fn managed(&self) -> ManagedHandle {
52 self.managed
53 }
54}
55
56#[derive(Debug)]
58pub enum InstanceError {
59 CaptureMismatch {
61 expected: usize,
63 actual: usize,
65 },
66 CaptureNameMismatch {
68 index: usize,
70 expected: String,
72 actual: String,
74 },
75 ManagedEdge(StrongEdgeMutationError),
77}
78
79impl fmt::Display for InstanceError {
80 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 Self::CaptureMismatch { expected, actual } => write!(
83 formatter,
84 "function plan declares {expected} captures but {actual} were supplied"
85 ),
86 Self::CaptureNameMismatch {
87 index,
88 expected,
89 actual,
90 } => write!(
91 formatter,
92 "function capture {index} is named {actual}, expected {expected}"
93 ),
94 Self::ManagedEdge(error) => write!(formatter, "cannot trace function capture: {error}"),
95 }
96 }
97}
98
99impl Error for InstanceError {}
100
101#[derive(Clone)]
103struct FunctionRole<B> {
104 plan: FunctionPlan,
105 body: B,
106 captures: Vec<CapturedBinding>,
107 class: ClassRef,
108 args_shape: Option<ShapeRef>,
109 result_shape: Option<ShapeRef>,
110}
111
112#[derive(Clone)]
119pub struct FunctionInstance<B: FunctionBodyPolicy> {
120 node: ManagedNode<FunctionRole<B>>,
121}
122
123impl<B: FunctionBodyPolicy> FunctionInstance<B> {
124 pub fn new(
126 plan: FunctionPlan,
127 body: B,
128 captures: Vec<CapturedBinding>,
129 class: ClassRef,
130 args_shape: Option<ShapeRef>,
131 result_shape: Option<ShapeRef>,
132 ) -> Result<Self, InstanceError> {
133 validate_capture_bindings(&plan, &captures)?;
134 let targets = captures
135 .iter()
136 .map(|capture| capture.managed().id())
137 .collect::<Vec<_>>();
138 let mut node = ManagedNode::new(FunctionRole {
139 plan,
140 body,
141 captures,
142 class,
143 args_shape,
144 result_shape,
145 });
146 for target in targets {
147 node.insert_strong(target)
148 .map_err(InstanceError::ManagedEdge)?;
149 }
150 Ok(Self { node })
151 }
152
153 pub const fn plan(&self) -> &FunctionPlan {
155 &self.node.role().plan
156 }
157
158 pub const fn body(&self) -> &B {
160 &self.node.role().body
161 }
162
163 pub fn captures(&self) -> &[CapturedBinding] {
165 &self.node.role().captures
166 }
167
168 pub const fn supplied_class(&self) -> &ClassRef {
170 &self.node.role().class
171 }
172
173 pub const fn args_shape(&self) -> Option<&ShapeRef> {
175 self.node.role().args_shape.as_ref()
176 }
177
178 pub const fn result_shape(&self) -> Option<&ShapeRef> {
180 self.node.role().result_shape.as_ref()
181 }
182
183 pub fn invoke_bound(&self, cx: &mut Cx, call: BoundCall) -> KernelResult<Value> {
188 self.body().invoke(cx, self.plan(), self.captures(), call)
189 }
190
191 pub(crate) fn invoke_values(&self, cx: &mut Cx, values: Vec<Value>) -> KernelResult<Value> {
192 self.invoke_bound(cx, bind(CallInput::from(Args::new(values))))
193 }
194}
195
196pub fn validate_capture_bindings(
198 plan: &FunctionPlan,
199 captures: &[CapturedBinding],
200) -> Result<(), InstanceError> {
201 if plan.captures().len() != captures.len() {
202 return Err(InstanceError::CaptureMismatch {
203 expected: plan.captures().len(),
204 actual: captures.len(),
205 });
206 }
207 for (index, (descriptor, capture)) in plan.captures().iter().zip(captures).enumerate() {
208 if descriptor.name() != capture.cell().name() {
209 return Err(InstanceError::CaptureNameMismatch {
210 index,
211 expected: descriptor.name().to_string(),
212 actual: capture.cell().name().to_string(),
213 });
214 }
215 }
216 Ok(())
217}
218
219impl<B: FunctionBodyPolicy> ManagedObject for FunctionInstance<B> {
220 fn trace_edges(&self, visitor: &mut dyn EdgeVisitor) {
221 self.node.trace_edges(visitor);
222 }
223
224 fn clear_weak_edge(&mut self, edge: EdgeId, expected: ManagedId) -> bool {
225 self.node.clear_weak_edge(edge, expected)
226 }
227
228 fn clear_ephemeron_edge(
229 &mut self,
230 edge: EdgeId,
231 expected_key: ManagedId,
232 expected_value: ManagedId,
233 ) -> bool {
234 self.node
235 .clear_ephemeron_edge(edge, expected_key, expected_value)
236 }
237}
238
239impl<B: FunctionBodyPolicy> Object for FunctionInstance<B> {
240 fn display(&self, _cx: &mut Cx) -> KernelResult<String> {
241 Ok(format!("#<function {}>", self.plan().display_identity()))
242 }
243
244 fn as_any(&self) -> &dyn Any {
245 self
246 }
247}
248
249impl<B: FunctionBodyPolicy> ObjectCompat for FunctionInstance<B> {
250 fn class(&self, _cx: &mut Cx) -> KernelResult<ClassRef> {
251 Ok(self.supplied_class().clone())
252 }
253
254 fn as_callable(&self) -> Option<&dyn Callable> {
255 Some(self)
256 }
257}
258
259impl<B: FunctionBodyPolicy> Callable for FunctionInstance<B> {
260 fn call(&self, cx: &mut Cx, args: Args) -> KernelResult<Value> {
261 self.invoke_values(cx, args.into_vec())
262 }
263
264 fn browse_args_shape(&self, _cx: &mut Cx) -> KernelResult<Option<ShapeRef>> {
265 Ok(self.args_shape().cloned())
266 }
267
268 fn browse_result_shape(&self, _cx: &mut Cx) -> KernelResult<Option<ShapeRef>> {
269 Ok(self.result_shape().cloned())
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use sim_kernel::{ShapeId, Symbol, testing::bare_cx};
276 use sim_lib_gc_tracing::{CollectionLimits, ManagedHeap};
277 use sim_lib_mutation::{EdgeSnapshot, ManagedNode};
278
279 use super::*;
280 use crate::CaptureDescriptor;
281
282 #[derive(Clone)]
283 struct EchoBody;
284
285 impl FunctionBodyPolicy for EchoBody {
286 fn invoke(
287 &self,
288 _cx: &mut Cx,
289 _plan: &FunctionPlan,
290 _captures: &[CapturedBinding],
291 call: BoundCall,
292 ) -> KernelResult<Value> {
293 match call.arguments()[0].input() {
294 crate::ArgumentInput::Positional(value) => Ok(value.clone()),
295 _ => unreachable!("kernel arguments are positional"),
296 }
297 }
298 }
299
300 fn plan(captures: usize) -> FunctionPlan {
301 FunctionPlan::new(
302 Symbol::new("guest:echo"),
303 Vec::new(),
304 (0..captures)
305 .map(|index| CaptureDescriptor::new(Symbol::new(format!("slot-{index}")), None))
306 .collect(),
307 Some(ShapeId(9)),
308 )
309 .unwrap()
310 }
311
312 fn metadata(cx: &mut Cx) -> (ClassRef, ShapeRef, ShapeRef) {
313 (
314 cx.factory().symbol(Symbol::new("guest-class")).unwrap(),
315 cx.factory().symbol(Symbol::new("args-shape")).unwrap(),
316 cx.factory().symbol(Symbol::new("result-shape")).unwrap(),
317 )
318 }
319
320 fn collection_limits() -> CollectionLimits {
321 CollectionLimits {
322 objects: 4,
323 edges: 4,
324 stack: 4,
325 work: 32,
326 clears: 4,
327 finalizers: 4,
328 }
329 }
330
331 #[test]
332 fn invocation_and_runtime_metadata_are_delegated_without_body_erasure() {
333 let mut cx = bare_cx();
334 let (class, args_shape, result_shape) = metadata(&mut cx);
335 let instance = FunctionInstance::new(
336 plan(0),
337 EchoBody,
338 Vec::new(),
339 class.clone(),
340 Some(args_shape.clone()),
341 Some(result_shape.clone()),
342 )
343 .unwrap();
344 let argument = cx.factory().symbol(Symbol::new("answer")).unwrap();
345
346 assert!(std::ptr::eq(instance.body(), &instance.node.role().body));
347 assert_eq!(instance.class(&mut cx).unwrap(), class);
348 assert_eq!(
349 instance.browse_args_shape(&mut cx).unwrap(),
350 Some(args_shape)
351 );
352 assert_eq!(
353 instance.browse_result_shape(&mut cx).unwrap(),
354 Some(result_shape)
355 );
356 assert_eq!(
357 instance
358 .call(&mut cx, Args::new(vec![argument.clone()]))
359 .unwrap(),
360 argument
361 );
362 }
363
364 #[test]
365 fn same_plan_instances_receive_distinct_managed_identities() {
366 let mut cx = bare_cx();
367 let (class, _, _) = metadata(&mut cx);
368 let mut heap = ManagedHeap::tracing(4, collection_limits()).unwrap();
369 let first = heap
370 .allocate(
371 FunctionInstance::new(plan(0), EchoBody, vec![], class.clone(), None, None)
372 .unwrap(),
373 )
374 .unwrap();
375 let second = heap
376 .allocate(FunctionInstance::new(plan(0), EchoBody, vec![], class, None, None).unwrap())
377 .unwrap();
378
379 assert_ne!(first.id(), second.id());
380 }
381
382 #[derive(Clone)]
383 enum CycleObject {
384 Function(FunctionInstance<EchoBody>),
385 Environment(ManagedNode<()>),
386 }
387
388 impl ManagedObject for CycleObject {
389 fn trace_edges(&self, visitor: &mut dyn EdgeVisitor) {
390 match self {
391 Self::Function(function) => function.trace_edges(visitor),
392 Self::Environment(environment) => environment.trace_edges(visitor),
393 }
394 }
395
396 fn clear_weak_edge(&mut self, edge: EdgeId, expected: ManagedId) -> bool {
397 match self {
398 Self::Function(function) => function.clear_weak_edge(edge, expected),
399 Self::Environment(environment) => environment.clear_weak_edge(edge, expected),
400 }
401 }
402
403 fn clear_ephemeron_edge(
404 &mut self,
405 edge: EdgeId,
406 expected_key: ManagedId,
407 expected_value: ManagedId,
408 ) -> bool {
409 match self {
410 Self::Function(function) => {
411 function.clear_ephemeron_edge(edge, expected_key, expected_value)
412 }
413 Self::Environment(environment) => {
414 environment.clear_ephemeron_edge(edge, expected_key, expected_value)
415 }
416 }
417 }
418 }
419
420 #[test]
421 fn closure_environment_cycle_is_collected_through_capture_edge() {
422 let mut cx = bare_cx();
423 let (class, _, _) = metadata(&mut cx);
424 let mut heap = ManagedHeap::tracing(4, collection_limits()).unwrap();
425 let environment = heap
426 .allocate(CycleObject::Environment(ManagedNode::new(())))
427 .unwrap();
428 let cell = BindingCell::uninitialized(Symbol::new("slot-0"));
429 let function = FunctionInstance::new(
430 plan(1),
431 EchoBody,
432 vec![CapturedBinding::new(cell, environment)],
433 class,
434 None,
435 None,
436 )
437 .unwrap();
438 assert_eq!(
439 function.node.edge_snapshot(),
440 vec![EdgeSnapshot::Strong {
441 edge: EdgeId(0),
442 target: environment.id(),
443 }]
444 );
445 let function = heap.allocate(CycleObject::Function(function)).unwrap();
446 match heap.get_mut(environment).unwrap() {
447 CycleObject::Environment(node) => {
448 node.insert_strong(function.id()).unwrap();
449 }
450 CycleObject::Function(_) => unreachable!(),
451 }
452
453 let receipt = heap.collect().unwrap().unwrap();
454 assert_eq!(receipt.swept, vec![environment.id(), function.id()]);
455 assert_eq!(heap.live_len(), 0);
456 }
457}