1use sim_lib_dispatch::{
4 AccessContext, AccessError, AccessorDescriptor, DataDescriptor, Descriptor, PropertyHook,
5 PropertyStore,
6};
7use std::collections::{BTreeMap, BTreeSet};
8
9#[derive(Clone, Debug, Default, Eq, PartialEq)]
13pub enum PythonObjectValue {
14 #[default]
16 None,
17 Int(i64),
19 String(String),
21 Object(u64),
23 Function(u64),
25 BoundMethod {
27 function: u64,
29 receiver: u64,
31 },
32}
33
34#[derive(Clone, Debug, Default, Eq, PartialEq)]
36pub struct DescriptorHook {
37 pub name: String,
39 pub value: PythonObjectValue,
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct PythonClass {
46 pub id: u64,
48 pub name: String,
50 pub bases: Vec<u64>,
52 pub mro: Vec<u64>,
54}
55
56#[derive(Clone, Debug, Eq, PartialEq)]
58pub enum ClassError {
59 UnknownBase(u64),
61 InconsistentMro,
63}
64
65#[derive(Clone, Debug, Eq, PartialEq)]
67pub enum AttributeError {
68 UnknownObject(u64),
70 UnknownClass(u64),
72 Missing(String),
74 Access,
76}
77
78#[derive(Default)]
79struct Hooks {
80 seen_receivers: Vec<u64>,
81}
82impl PropertyHook<u64, String, PythonObjectValue, DescriptorHook> for Hooks {
83 type Error = ();
84 fn get(
85 &mut self,
86 _: &mut AccessContext<u64, String>,
87 hook: &DescriptorHook,
88 receiver: &u64,
89 _: &String,
90 ) -> Result<PythonObjectValue, AccessError<Self::Error>> {
91 self.seen_receivers.push(*receiver);
92 Ok(hook.value.clone())
93 }
94 fn set(
95 &mut self,
96 _: &mut AccessContext<u64, String>,
97 _: &DescriptorHook,
98 receiver: &u64,
99 _: &String,
100 _: PythonObjectValue,
101 ) -> Result<(), AccessError<Self::Error>> {
102 self.seen_receivers.push(*receiver);
103 Ok(())
104 }
105}
106
107#[derive(Default)]
109pub struct PythonObjectSpace {
110 classes: BTreeMap<u64, PythonClass>,
111 instances: BTreeMap<u64, u64>,
112 properties: PropertyStore<u64, String, PythonObjectValue, DescriptorHook>,
113 hooks: Hooks,
114}
115
116impl PythonObjectSpace {
117 pub fn define_class(
119 &mut self,
120 id: u64,
121 name: impl Into<String>,
122 bases: Vec<u64>,
123 ) -> Result<(), ClassError> {
124 let mut sequences = Vec::new();
125 for base in &bases {
126 sequences.push(
127 self.classes
128 .get(base)
129 .ok_or(ClassError::UnknownBase(*base))?
130 .mro
131 .clone(),
132 );
133 }
134 sequences.push(bases.clone());
135 let mut mro = vec![id];
136 mro.extend(c3_merge(sequences)?);
137 self.classes.insert(
138 id,
139 PythonClass {
140 id,
141 name: name.into(),
142 bases,
143 mro,
144 },
145 );
146 Ok(())
147 }
148
149 pub fn instantiate(&mut self, object: u64, class: u64) -> Result<(), AttributeError> {
151 if !self.classes.contains_key(&class) {
152 return Err(AttributeError::UnknownClass(class));
153 }
154 self.instances.insert(object, class);
155 Ok(())
156 }
157
158 pub fn class(&self, id: u64) -> Option<&PythonClass> {
160 self.classes.get(&id)
161 }
162
163 pub fn define_value(&mut self, owner: u64, key: impl Into<String>, value: PythonObjectValue) {
165 self.properties
166 .define(
167 &owner,
168 key.into(),
169 Descriptor::Data(DataDescriptor {
170 value,
171 writable: true,
172 enumerable: true,
173 configurable: true,
174 }),
175 )
176 .expect("configurable Python value accepts replacement");
177 }
178
179 pub fn define_descriptor(
181 &mut self,
182 class: u64,
183 key: impl Into<String>,
184 getter: DescriptorHook,
185 data: bool,
186 ) {
187 self.properties
188 .define(
189 &class,
190 key.into(),
191 Descriptor::Accessor(AccessorDescriptor {
192 get: Some(getter),
193 set: data.then(|| DescriptorHook {
194 name: "set".into(),
195 value: PythonObjectValue::None,
196 }),
197 enumerable: true,
198 configurable: true,
199 }),
200 )
201 .expect("configurable Python descriptor accepts replacement");
202 }
203
204 pub fn get(&mut self, object: u64, key: &str) -> Result<PythonObjectValue, AttributeError> {
207 let class = *self
208 .instances
209 .get(&object)
210 .ok_or(AttributeError::UnknownObject(object))?;
211 let mro = self
212 .classes
213 .get(&class)
214 .ok_or(AttributeError::UnknownClass(class))?
215 .mro
216 .clone();
217 let key = key.to_owned();
218 let descriptor_owner = mro
219 .iter()
220 .find(|owner| self.properties.own(owner, &key).is_some())
221 .copied();
222 if let Some(owner) = descriptor_owner
223 && matches!(self.properties.own(&owner, &key), Some(Descriptor::Accessor(a)) if a.set.is_some())
224 {
225 return self.read(&[owner], object, &key);
226 }
227 if self.properties.own(&object, &key).is_some() {
228 return self.read(&[object], object, &key);
229 }
230 let value = self.read(&mro, object, &key)?;
231 Ok(match value {
232 PythonObjectValue::Function(function) => PythonObjectValue::BoundMethod {
233 function,
234 receiver: object,
235 },
236 other => other,
237 })
238 }
239
240 pub fn get_super(
242 &mut self,
243 current: u64,
244 object: u64,
245 key: &str,
246 ) -> Result<PythonObjectValue, AttributeError> {
247 let class = *self
248 .instances
249 .get(&object)
250 .ok_or(AttributeError::UnknownObject(object))?;
251 let mro = &self
252 .classes
253 .get(&class)
254 .ok_or(AttributeError::UnknownClass(class))?
255 .mro;
256 let at = mro
257 .iter()
258 .position(|candidate| *candidate == current)
259 .ok_or(AttributeError::UnknownClass(current))?;
260 let owners = mro[at + 1..].to_vec();
261 let value = self.read(&owners, object, &key.to_owned())?;
262 Ok(match value {
263 PythonObjectValue::Function(function) => PythonObjectValue::BoundMethod {
264 function,
265 receiver: object,
266 },
267 other => other,
268 })
269 }
270
271 fn read(
272 &mut self,
273 owners: &[u64],
274 receiver: u64,
275 key: &String,
276 ) -> Result<PythonObjectValue, AttributeError> {
277 self.properties
278 .get(
279 owners,
280 &receiver,
281 key,
282 &mut AccessContext::new(64),
283 &mut self.hooks,
284 )
285 .map_err(|_| AttributeError::Access)?
286 .ok_or_else(|| AttributeError::Missing(key.clone()))
287 }
288}
289
290fn c3_merge(mut sequences: Vec<Vec<u64>>) -> Result<Vec<u64>, ClassError> {
291 let mut result = Vec::new();
292 loop {
293 sequences.retain(|sequence| !sequence.is_empty());
294 if sequences.is_empty() {
295 return Ok(result);
296 }
297 let candidate = sequences
298 .iter()
299 .map(|sequence| sequence[0])
300 .find(|candidate| {
301 sequences
302 .iter()
303 .all(|sequence| !sequence[1..].contains(candidate))
304 })
305 .ok_or(ClassError::InconsistentMro)?;
306 result.push(candidate);
307 for sequence in &mut sequences {
308 if sequence.first() == Some(&candidate) {
309 sequence.remove(0);
310 }
311 }
312 let unique: BTreeSet<_> = result.iter().copied().collect();
313 if unique.len() != result.len() {
314 return Err(ClassError::InconsistentMro);
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 fn value(value: i64) -> PythonObjectValue {
323 PythonObjectValue::Int(value)
324 }
325 #[test]
326 fn c3_descriptors_binding_and_super_share_property_mechanics() {
327 let mut space = PythonObjectSpace::default();
328 space.define_class(1, "object", vec![]).unwrap();
329 space.define_class(2, "Left", vec![1]).unwrap();
330 space.define_class(3, "Right", vec![1]).unwrap();
331 space.define_class(4, "Diamond", vec![2, 3]).unwrap();
332 assert_eq!(space.class(4).unwrap().mro, vec![4, 2, 3, 1]);
333 space.instantiate(10, 4).unwrap();
334 space.define_descriptor(
335 2,
336 "data",
337 DescriptorHook {
338 name: "data".into(),
339 value: value(1),
340 },
341 true,
342 );
343 space.define_descriptor(
344 2,
345 "nondata",
346 DescriptorHook {
347 name: "nondata".into(),
348 value: value(2),
349 },
350 false,
351 );
352 space.define_value(10, "data", value(11));
353 space.define_value(10, "nondata", value(22));
354 assert_eq!(space.get(10, "data"), Ok(value(1)));
355 assert_eq!(space.get(10, "nondata"), Ok(value(22)));
356 space.define_value(3, "method", PythonObjectValue::Function(7));
357 assert_eq!(
358 space.get_super(2, 10, "method"),
359 Ok(PythonObjectValue::BoundMethod {
360 function: 7,
361 receiver: 10
362 })
363 );
364 assert_eq!(space.hooks.seen_receivers, vec![10]);
365 }
366
367 #[test]
368 fn inconsistent_c3_fails_explicitly() {
369 let mut space = PythonObjectSpace::default();
370 space.define_class(1, "object", vec![]).unwrap();
371 space.define_class(2, "A", vec![1]).unwrap();
372 space.define_class(3, "B", vec![1]).unwrap();
373 space.define_class(4, "AB", vec![2, 3]).unwrap();
374 space.define_class(5, "BA", vec![3, 2]).unwrap();
375 assert_eq!(
376 space.define_class(6, "Impossible", vec![4, 5]),
377 Err(ClassError::InconsistentMro)
378 );
379 }
380}