Skip to main content

sim_lib_class/
protocol.rs

1//! Projection of checked descriptors onto the kernel [`Class`] protocol.
2
3use std::{any::Any, collections::BTreeSet, sync::Arc};
4
5use sim_kernel::{
6    Args, Callable, Class, ClassId, ClassRef, Cx, Error, Object, ObjectCompat, ReadConstructorRef,
7    Result, ShapeRef, Symbol, TableRef, Value,
8};
9
10use crate::ClassDescriptor;
11
12/// Construction behavior supplied by the language or library declaring a class.
13pub trait ClassConstructor: Send + Sync + 'static {
14    fn construct(&self, cx: &mut Cx, args: Args) -> Result<Value>;
15}
16
17impl<F> ClassConstructor for F
18where
19    F: Fn(&mut Cx, Args) -> Result<Value> + Send + Sync + 'static,
20{
21    fn construct(&self, cx: &mut Cx, args: Args) -> Result<Value> {
22        self(cx, args)
23    }
24}
25
26/// Evidence retained for every bounded subclass query.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct SubclassEvidence {
29    pub visited_nodes: usize,
30    pub performed_work: usize,
31}
32
33/// Result of a subclass query, including exact exhaustion evidence.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub enum SubclassQuery {
36    Answer {
37        is_subclass: bool,
38        evidence: SubclassEvidence,
39    },
40    NodeBudgetExhausted {
41        limit: usize,
42        required: usize,
43        evidence: SubclassEvidence,
44    },
45    WorkBudgetExhausted {
46        limit: usize,
47        evidence: SubclassEvidence,
48    },
49}
50
51/// A checked descriptor exposed as an ordinary kernel class object.
52pub struct DescriptorClass {
53    descriptor: ClassDescriptor,
54    constructor: Arc<dyn ClassConstructor>,
55    subclass_nodes: usize,
56    subclass_work: usize,
57}
58
59impl DescriptorClass {
60    pub fn new(
61        descriptor: ClassDescriptor,
62        constructor: impl ClassConstructor,
63        subclass_nodes: usize,
64        subclass_work: usize,
65    ) -> Self {
66        Self {
67            descriptor,
68            constructor: Arc::new(constructor),
69            subclass_nodes,
70            subclass_work,
71        }
72    }
73
74    pub fn descriptor(&self) -> &ClassDescriptor {
75        &self.descriptor
76    }
77
78    /// Runs the kernel subclass relation with caller-visible finite limits.
79    pub fn query_subclass(
80        &self,
81        cx: &mut Cx,
82        expected: ClassRef,
83        node_limit: usize,
84        work_limit: usize,
85    ) -> Result<SubclassQuery> {
86        let Some(expected) = expected.object().as_class() else {
87            return Ok(SubclassQuery::Answer {
88                is_subclass: false,
89                evidence: SubclassEvidence {
90                    visited_nodes: 0,
91                    performed_work: 0,
92                },
93            });
94        };
95        let mut stack = vec![self.parents(cx)?];
96        let mut seen = BTreeSet::from([self.id()]);
97        let mut evidence = SubclassEvidence {
98            visited_nodes: 1,
99            performed_work: 0,
100        };
101        if self.id() == expected.id() {
102            return Ok(SubclassQuery::Answer {
103                is_subclass: true,
104                evidence,
105            });
106        }
107
108        while let Some(parents) = stack.last_mut() {
109            let Some(parent) = parents.pop() else {
110                stack.pop();
111                continue;
112            };
113            if evidence.performed_work == work_limit {
114                return Ok(SubclassQuery::WorkBudgetExhausted {
115                    limit: work_limit,
116                    evidence,
117                });
118            }
119            evidence.performed_work += 1;
120            let Some(parent) = parent.object().as_class() else {
121                continue;
122            };
123            if !seen.insert(parent.id()) {
124                continue;
125            }
126            let required = evidence.visited_nodes + 1;
127            if required > node_limit {
128                return Ok(SubclassQuery::NodeBudgetExhausted {
129                    limit: node_limit,
130                    required,
131                    evidence,
132                });
133            }
134            evidence.visited_nodes = required;
135            if parent.id() == expected.id() {
136                return Ok(SubclassQuery::Answer {
137                    is_subclass: true,
138                    evidence,
139                });
140            }
141            stack.push(parent.parents(cx)?);
142        }
143        Ok(SubclassQuery::Answer {
144            is_subclass: false,
145            evidence,
146        })
147    }
148}
149
150impl Object for DescriptorClass {
151    fn display(&self, _cx: &mut Cx) -> Result<String> {
152        Ok(format!("#<class {}>", self.descriptor.identity().symbol()))
153    }
154
155    fn as_any(&self) -> &dyn Any {
156        self
157    }
158}
159
160impl ObjectCompat for DescriptorClass {
161    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
162        cx.resolve_class(&Symbol::qualified("core", "Class"))
163    }
164    fn as_callable(&self) -> Option<&dyn Callable> {
165        Some(self)
166    }
167    fn as_class(&self) -> Option<&dyn Class> {
168        Some(self)
169    }
170}
171
172impl Callable for DescriptorClass {
173    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
174        self.constructor.construct(cx, args)
175    }
176    fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
177        Ok(Some(self.descriptor.constructor_shape().clone()))
178    }
179    fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
180        Ok(Some(self.descriptor.instance_shape().clone()))
181    }
182}
183
184impl Class for DescriptorClass {
185    fn id(&self) -> ClassId {
186        self.descriptor.identity().id()
187    }
188    fn symbol(&self) -> Symbol {
189        self.descriptor.identity().symbol().clone()
190    }
191    fn parents(&self, _cx: &mut Cx) -> Result<Vec<ClassRef>> {
192        Ok(self
193            .descriptor
194            .parents()
195            .iter()
196            .filter_map(|parent| parent.resolved_class().cloned())
197            .collect())
198    }
199    fn is_subclass_of(&self, cx: &mut Cx, expected: ClassRef) -> Result<bool> {
200        match self.query_subclass(cx, expected, self.subclass_nodes, self.subclass_work)? {
201            SubclassQuery::Answer { is_subclass, .. } => Ok(is_subclass),
202            evidence => Err(Error::Lib(format!(
203                "bounded class lineage exhausted: {evidence:?}"
204            ))),
205        }
206    }
207    fn constructor_shape(&self, _cx: &mut Cx) -> Result<ShapeRef> {
208        Ok(self.descriptor.constructor_shape().clone())
209    }
210    fn instance_shape(&self, _cx: &mut Cx) -> Result<ShapeRef> {
211        Ok(self.descriptor.instance_shape().clone())
212    }
213    fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
214        Ok(self
215            .descriptor
216            .read_construction()
217            .map(|read| read.constructor.clone()))
218    }
219    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
220        cx.factory().table(
221            self.descriptor
222                .members()
223                .iter()
224                .map(|member| (member.name.clone(), member.shape.clone()))
225                .collect(),
226        )
227    }
228}