Skip to main content

gc/
value.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt;
3use std::fmt::Write as _;
4use std::rc::Rc;
5
6use object::builtins::{BuiltIns, BuiltinId};
7use object::{Closure, CompiledFunction, Object};
8use serde::Serialize;
9
10use crate::header::GcObjectType;
11use crate::{GcHeap, GcObject, GcRef};
12
13/// Runtime value stored in the GC heap. Mirrors `object::Object` but uses `GcRef` edges.
14#[derive(Debug, Clone, PartialEq)]
15pub enum Value {
16    Integer(i64),
17    Boolean(bool),
18    String(String),
19    Array(Vec<GcRef>),
20    Hash(HashMap<HashKey, GcRef>),
21    Null,
22    Error(String),
23    CompiledFunction(CompiledFunction),
24    Closure(GcClosure),
25    Builtin(BuiltinId),
26    Class(GcClass),
27    Instance(GcInstance),
28    BoundMethod(GcBoundMethod),
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct GcClosure {
33    pub func: GcRef,
34    pub free: Vec<GcRef>,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct GcClass {
39    pub name: String,
40    pub constructor: Option<GcRef>,
41    pub methods: HashMap<String, GcRef>,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct GcInstance {
46    pub class: GcRef,
47    pub fields: HashMap<String, GcRef>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct GcBoundMethod {
52    pub receiver: GcRef,
53    pub method: GcRef,
54    pub name: String,
55}
56
57#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
58#[serde(rename_all = "camelCase")]
59pub enum ValueKind {
60    Class,
61    Instance,
62    BoundMethod,
63    Closure,
64    Array,
65    Hash,
66    Integer,
67    Boolean,
68    String,
69    Null,
70    Error,
71    CompiledFunction,
72    Builtin,
73    Other,
74}
75
76/// Typed heap-edge relation used by teaching diagnostics.
77#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
78#[serde(tag = "kind", rename_all = "camelCase")]
79pub enum EdgeRelation {
80    ArrayElement {
81        index: usize,
82    },
83    HashValue {
84        #[serde(rename = "keyKind")]
85        key_kind: HashKeyKind,
86        key: String,
87    },
88    ClosureFunction,
89    ClosureFree {
90        index: usize,
91    },
92    ClassConstructor,
93    ClassMethod {
94        name: String,
95    },
96    InstanceClass,
97    InstanceField {
98        name: String,
99    },
100    BoundMethodReceiver,
101    BoundMethodFunction,
102    Unknown,
103}
104
105#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
106#[serde(rename_all = "camelCase")]
107pub enum HashKeyKind {
108    Integer,
109    Boolean,
110    String,
111}
112
113pub const MAX_HASH_KEY_LABEL_LEN: usize = 64;
114
115pub fn format_hash_key_label(key: &HashKey) -> String {
116    match key {
117        HashKey::Integer(value) => value.to_string(),
118        HashKey::Boolean(value) => value.to_string(),
119        HashKey::String(value) => escape_and_truncate_key(value),
120    }
121}
122
123fn escape_and_truncate_key(value: &str) -> String {
124    let mut escaped = String::with_capacity(value.len().min(MAX_HASH_KEY_LABEL_LEN) + 3);
125    for ch in value.chars() {
126        let mut char_buffer = [0; 4];
127        let mut control_buffer = String::new();
128        let encoded = match ch {
129            '\\' => "\\\\",
130            '"' => "\\\"",
131            '\n' => "\\n",
132            '\r' => "\\r",
133            '\t' => "\\t",
134            c if c.is_control() => {
135                write!(&mut control_buffer, "\\u{:04x}", c as u32)
136                    .expect("writing to a String cannot fail");
137                control_buffer.as_str()
138            }
139            c => c.encode_utf8(&mut char_buffer),
140        };
141        if escaped.len() + encoded.len() > MAX_HASH_KEY_LABEL_LEN {
142            escaped.push('…');
143            break;
144        }
145        escaped.push_str(encoded);
146    }
147    escaped
148}
149
150#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
151pub enum HashKey {
152    Integer(i64),
153    Boolean(bool),
154    String(String),
155}
156
157impl HashKey {
158    pub fn kind(&self) -> HashKeyKind {
159        match self {
160            HashKey::Integer(_) => HashKeyKind::Integer,
161            HashKey::Boolean(_) => HashKeyKind::Boolean,
162            HashKey::String(_) => HashKeyKind::String,
163        }
164    }
165}
166
167pub struct ValueCell {
168    pub value: Value,
169}
170
171impl GcObject for ValueCell {
172    fn trace(&self, visit: &mut dyn FnMut(crate::GcId)) {
173        self.value.trace(&mut |reference| visit(reference.0));
174    }
175}
176
177impl Value {
178    pub fn kind(&self) -> ValueKind {
179        match self {
180            Value::Class(_) => ValueKind::Class,
181            Value::Instance(_) => ValueKind::Instance,
182            Value::BoundMethod(_) => ValueKind::BoundMethod,
183            Value::Closure(_) => ValueKind::Closure,
184            Value::Array(_) => ValueKind::Array,
185            Value::Hash(_) => ValueKind::Hash,
186            Value::Integer(_) => ValueKind::Integer,
187            Value::Boolean(_) => ValueKind::Boolean,
188            Value::String(_) => ValueKind::String,
189            Value::Null => ValueKind::Null,
190            Value::Error(_) => ValueKind::Error,
191            Value::CompiledFunction(_) => ValueKind::CompiledFunction,
192            Value::Builtin(_) => ValueKind::Builtin,
193        }
194    }
195
196    /// Visit heap edges with typed structural relations for diagnostics.
197    ///
198    /// Hash keys, class methods, and instance fields are visited in stable
199    /// sorted order so reports stay deterministic. The collector's `trace`
200    /// path remains allocation-free and visits only edge targets.
201    pub fn visit_edges(&self, mut visit: impl FnMut(EdgeRelation, GcRef)) {
202        match self {
203            Value::Array(items) => {
204                for (index, item) in items.iter().enumerate() {
205                    visit(
206                        EdgeRelation::ArrayElement {
207                            index,
208                        },
209                        *item,
210                    );
211                }
212            }
213            Value::Hash(map) => {
214                let mut entries = map.iter().collect::<Vec<_>>();
215                entries.sort_by(|(left, _), (right, _)| left.cmp(right));
216                for (key, value) in entries {
217                    visit(
218                        EdgeRelation::HashValue {
219                            key_kind: key.kind(),
220                            key: format_hash_key_label(key),
221                        },
222                        *value,
223                    );
224                }
225            }
226            Value::Closure(closure) => {
227                visit(EdgeRelation::ClosureFunction, closure.func);
228                for (index, free) in closure.free.iter().enumerate() {
229                    visit(
230                        EdgeRelation::ClosureFree {
231                            index,
232                        },
233                        *free,
234                    );
235                }
236            }
237            Value::Class(class) => {
238                if let Some(constructor) = class.constructor {
239                    visit(EdgeRelation::ClassConstructor, constructor);
240                }
241                let mut methods = class.methods.iter().collect::<Vec<_>>();
242                methods.sort_by(|(left, _), (right, _)| left.cmp(right));
243                for (name, method) in methods {
244                    visit(
245                        EdgeRelation::ClassMethod {
246                            name: name.clone(),
247                        },
248                        *method,
249                    );
250                }
251            }
252            Value::Instance(instance) => {
253                visit(EdgeRelation::InstanceClass, instance.class);
254                let mut fields = instance.fields.iter().collect::<Vec<_>>();
255                fields.sort_by(|(left, _), (right, _)| left.cmp(right));
256                for (name, value) in fields {
257                    visit(
258                        EdgeRelation::InstanceField {
259                            name: name.clone(),
260                        },
261                        *value,
262                    );
263                }
264            }
265            Value::BoundMethod(method) => {
266                visit(EdgeRelation::BoundMethodReceiver, method.receiver);
267                visit(EdgeRelation::BoundMethodFunction, method.method);
268            }
269            // Leaf variants own no GcRef. No catch-all: adding a Value variant
270            // must fail to compile until its edges are classified here.
271            Value::Integer(_)
272            | Value::Boolean(_)
273            | Value::String(_)
274            | Value::Null
275            | Value::Error(_)
276            | Value::CompiledFunction(_)
277            | Value::Builtin(_) => {}
278        }
279    }
280
281    pub fn trace(&self, visit: &mut dyn FnMut(GcRef)) {
282        match self {
283            Value::Array(items) => {
284                for item in items {
285                    visit(*item);
286                }
287            }
288            Value::Hash(map) => {
289                for value in map.values() {
290                    visit(*value);
291                }
292            }
293            Value::Closure(closure) => {
294                visit(closure.func);
295                for free in &closure.free {
296                    visit(*free);
297                }
298            }
299            Value::Class(class) => {
300                if let Some(constructor) = class.constructor {
301                    visit(constructor);
302                }
303                for method in class.methods.values() {
304                    visit(*method);
305                }
306            }
307            Value::Instance(instance) => {
308                visit(instance.class);
309                for field in instance.fields.values() {
310                    visit(*field);
311                }
312            }
313            Value::BoundMethod(method) => {
314                visit(method.receiver);
315                visit(method.method);
316            }
317            // Leaf variants own no GcRef. No catch-all: adding a Value variant
318            // must fail to compile until its edges are classified here.
319            Value::Integer(_)
320            | Value::Boolean(_)
321            | Value::String(_)
322            | Value::Null
323            | Value::Error(_)
324            | Value::CompiledFunction(_)
325            | Value::Builtin(_) => {}
326        }
327    }
328
329    pub fn with_owned_edges(self, heap: &mut GcHeap) -> Self {
330        match self {
331            Value::Array(items) => Value::Array(items.into_iter().map(|r| heap.dup(r)).collect()),
332            Value::Hash(map) => {
333                Value::Hash(map.into_iter().map(|(k, v)| (k, heap.dup(v))).collect())
334            }
335            Value::Closure(mut closure) => {
336                closure.func = heap.dup(closure.func);
337                closure.free = closure.free.into_iter().map(|r| heap.dup(r)).collect();
338                Value::Closure(closure)
339            }
340            Value::Class(mut class) => {
341                class.constructor = class.constructor.map(|r| heap.dup(r));
342                class.methods = class
343                    .methods
344                    .into_iter()
345                    .map(|(name, method)| (name, heap.dup(method)))
346                    .collect();
347                Value::Class(class)
348            }
349            Value::Instance(mut instance) => {
350                instance.class = heap.dup(instance.class);
351                instance.fields = instance
352                    .fields
353                    .into_iter()
354                    .map(|(name, value)| (name, heap.dup(value)))
355                    .collect();
356                Value::Instance(instance)
357            }
358            Value::BoundMethod(mut method) => {
359                method.receiver = heap.dup(method.receiver);
360                method.method = heap.dup(method.method);
361                Value::BoundMethod(method)
362            }
363            other => other,
364        }
365    }
366
367    pub fn edge_refs(&self) -> Vec<GcRef> {
368        let mut refs = Vec::new();
369        self.trace(&mut |reference| refs.push(reference));
370        refs
371    }
372}
373
374impl HashKey {
375    pub fn from_object(object: &Object) -> Option<HashKey> {
376        match object {
377            Object::Integer(i) => Some(HashKey::Integer(*i)),
378            Object::Boolean(b) => Some(HashKey::Boolean(*b)),
379            Object::String(s) => Some(HashKey::String(s.clone())),
380            _ => None,
381        }
382    }
383
384    pub fn from_value(value: &Value) -> Option<HashKey> {
385        match value {
386            Value::Integer(i) => Some(HashKey::Integer(*i)),
387            Value::Boolean(b) => Some(HashKey::Boolean(*b)),
388            Value::String(s) => Some(HashKey::String(s.clone())),
389            _ => None,
390        }
391    }
392
393    pub fn to_object(&self) -> Object {
394        match self {
395            HashKey::Integer(i) => Object::Integer(*i),
396            HashKey::Boolean(b) => Object::Boolean(*b),
397            HashKey::String(s) => Object::String(s.clone()),
398        }
399    }
400}
401
402pub fn alloc_value(heap: &mut GcHeap, value: Value) -> GcRef {
403    let value = value.with_owned_edges(heap);
404    heap.alloc(
405        ValueCell {
406            value,
407        },
408        GcObjectType::MonkeyObject,
409    )
410}
411
412pub fn get_value(heap: &GcHeap, reference: GcRef) -> &Value {
413    &heap
414        .runtime()
415        .object_downcast::<ValueCell>(reference.0)
416        .expect("invalid value reference")
417        .value
418}
419
420pub fn get_value_mut(heap: &mut GcHeap, reference: GcRef) -> &mut Value {
421    &mut heap
422        .runtime_mut()
423        .object_downcast_mut::<ValueCell>(reference.0)
424        .expect("invalid value reference")
425        .value
426}
427
428pub fn value_to_string(heap: &GcHeap, reference: GcRef) -> String {
429    format_reference(heap, reference, &mut HashSet::new())
430}
431
432fn format_reference(heap: &GcHeap, reference: GcRef, visited: &mut HashSet<usize>) -> String {
433    if !visited.insert(reference.0) {
434        return format!("[cycle #{}]", reference.0);
435    }
436    let formatted = format_value(heap, get_value(heap, reference), visited);
437    visited.remove(&reference.0);
438    formatted
439}
440
441fn format_value(heap: &GcHeap, value: &Value, visited: &mut HashSet<usize>) -> String {
442    match value {
443        Value::Integer(i) => i.to_string(),
444        Value::Boolean(b) => b.to_string(),
445        Value::String(s) => s.clone(),
446        Value::Null => "null".to_string(),
447        Value::Error(e) => e.clone(),
448        Value::Array(items) => {
449            let parts = items
450                .iter()
451                .map(|item| format_reference(heap, *item, visited))
452                .collect::<Vec<_>>()
453                .join(", ");
454            format!("[{}]", parts)
455        }
456        Value::Hash(map) => {
457            let parts = map
458                .iter()
459                .map(|(k, v)| {
460                    format!("{}: {}", format_hash_key(k), format_reference(heap, *v, visited))
461                })
462                .collect::<Vec<_>>()
463                .join(", ");
464            format!("{{{}}}", parts)
465        }
466        Value::CompiledFunction(_) => "[compiled function]".to_string(),
467        Value::Closure(_) => "[closure function]".to_string(),
468        Value::Builtin(_) => "[builtin function]".to_string(),
469        Value::Class(class) => format!("[class {}]", class.name),
470        Value::Instance(instance) => {
471            format!("[object {}]", class_name(heap, instance.class))
472        }
473        Value::BoundMethod(method) => {
474            format!("[bound method {}.{}]", instance_class_name(heap, method.receiver), method.name)
475        }
476    }
477}
478
479fn class_name(heap: &GcHeap, class: GcRef) -> String {
480    match get_value(heap, class) {
481        Value::Class(class) => class.name.clone(),
482        _ => "<invalid class>".to_string(),
483    }
484}
485
486fn instance_class_name(heap: &GcHeap, instance: GcRef) -> String {
487    match get_value(heap, instance) {
488        Value::Instance(instance) => class_name(heap, instance.class),
489        _ => "<invalid receiver>".to_string(),
490    }
491}
492
493fn format_hash_key(key: &HashKey) -> String {
494    match key {
495        HashKey::Integer(i) => i.to_string(),
496        HashKey::Boolean(b) => b.to_string(),
497        HashKey::String(s) => s.clone(),
498    }
499}
500
501pub fn import_object(heap: &mut GcHeap, object: &Object) -> GcRef {
502    let value = match object {
503        Object::Integer(i) => Value::Integer(*i),
504        Object::Boolean(b) => Value::Boolean(*b),
505        Object::String(s) => Value::String(s.clone()),
506        Object::Null => Value::Null,
507        Object::Error(e) => Value::Error(e.clone()),
508        Object::Array(items) => {
509            Value::Array(items.iter().map(|item| import_object(heap, item)).collect())
510        }
511        Object::Hash(map) => Value::Hash(
512            map.iter()
513                .map(|(k, v)| {
514                    (
515                        HashKey::from_object(k).expect("hash key must be hashable"),
516                        import_object(heap, v),
517                    )
518                })
519                .collect(),
520        ),
521        Object::CompiledFunction(f) => Value::CompiledFunction(CompiledFunction {
522            name: f.name.clone(),
523            instructions: f.instructions.clone(),
524            num_locals: f.num_locals,
525            num_parameters: f.num_parameters,
526        }),
527        Object::ClosureObj(closure) => Value::Closure(GcClosure {
528            func: import_object(heap, &Object::CompiledFunction(Rc::clone(&closure.func))),
529            free: closure
530                .free
531                .iter()
532                .map(|item| import_object(heap, item))
533                .collect(),
534        }),
535        Object::Builtin(function) => {
536            let definition = BuiltIns
537                .iter()
538                .find(|definition| std::ptr::fn_addr_eq(definition.function, *function))
539                .expect("unknown builtin function");
540            Value::Builtin(definition.id)
541        }
542        Object::ReturnValue(inner) => return import_object(heap, inner),
543        Object::Function(_, _, _) => {
544            panic!("interpreter functions cannot be imported into the GC VM")
545        }
546        Object::Class(_) | Object::Instance(_) | Object::BoundMethod(_) => {
547            panic!("graph values cannot be imported into the GC VM")
548        }
549    };
550    let edge_refs = value.edge_refs();
551    let reference = alloc_value(heap, value);
552    for edge in edge_refs {
553        heap.free(edge);
554    }
555    reference
556}
557
558pub fn try_export_object(heap: &GcHeap, reference: GcRef) -> Result<Object, String> {
559    match get_value(heap, reference) {
560        Value::Integer(i) => Ok(Object::Integer(*i)),
561        Value::Boolean(b) => Ok(Object::Boolean(*b)),
562        Value::String(s) => Ok(Object::String(s.clone())),
563        Value::Null => Ok(Object::Null),
564        Value::Error(e) => Ok(Object::Error(e.clone())),
565        Value::Array(items) => {
566            let mut exported = Vec::with_capacity(items.len());
567            for item in items {
568                exported.push(Rc::new(try_export_object(heap, *item)?));
569            }
570            Ok(Object::Array(exported))
571        }
572        Value::Hash(map) => {
573            let mut exported = HashMap::with_capacity(map.len());
574            for (key, value) in map {
575                exported
576                    .insert(Rc::new(key.to_object()), Rc::new(try_export_object(heap, *value)?));
577            }
578            Ok(Object::Hash(exported))
579        }
580        Value::CompiledFunction(f) => Ok(Object::CompiledFunction(Rc::new(f.clone()))),
581        Value::Closure(closure) => {
582            let func = match get_value(heap, closure.func) {
583                Value::CompiledFunction(f) => Rc::new(f.clone()),
584                _ => return Err("closure func must be compiled function".to_string()),
585            };
586            let mut free = Vec::with_capacity(closure.free.len());
587            for item in &closure.free {
588                free.push(Rc::new(try_export_object(heap, *item)?));
589            }
590            Ok(Object::ClosureObj(Closure {
591                func,
592                free,
593            }))
594        }
595        Value::Builtin(id) => {
596            let definition = BuiltIns
597                .iter()
598                .find(|definition| definition.id == *id)
599                .ok_or_else(|| "unknown builtin id".to_string())?;
600            Ok(Object::Builtin(definition.function))
601        }
602        Value::Class(_) | Value::Instance(_) | Value::BoundMethod(_) => {
603            Err("GC graph values cannot be exported as object::Object".to_string())
604        }
605    }
606}
607
608pub fn export_object(heap: &GcHeap, reference: GcRef) -> Object {
609    try_export_object(heap, reference).expect("value cannot be exported")
610}
611
612pub fn call_builtin(heap: &mut GcHeap, builtin: BuiltinId, args: &[GcRef], null: GcRef) -> GcRef {
613    match builtin {
614        BuiltinId::Len => {
615            if args.len() != 1 {
616                return alloc_value(
617                    heap,
618                    Value::Error(format!("builtin len expected 1 argument, got {}", args.len())),
619                );
620            }
621            match get_value(heap, args[0]) {
622                Value::String(value) => alloc_value(heap, Value::Integer(value.len() as i64)),
623                Value::Array(value) => alloc_value(heap, Value::Integer(value.len() as i64)),
624                _ => alloc_value(
625                    heap,
626                    Value::Error(format!(
627                        "builtin len not supported for for type {}",
628                        value_to_string(heap, args[0])
629                    )),
630                ),
631            }
632        }
633        BuiltinId::Puts => {
634            for argument in args {
635                println!("{}", value_to_string(heap, *argument));
636            }
637            heap.dup(null)
638        }
639        BuiltinId::First | BuiltinId::Last | BuiltinId::Rest => {
640            let name = match builtin {
641                BuiltinId::First => "first",
642                BuiltinId::Last => "last",
643                BuiltinId::Rest => "rest",
644                _ => unreachable!(),
645            };
646            if args.len() != 1 {
647                return alloc_value(
648                    heap,
649                    Value::Error(format!(
650                        "builtin {} expected 1 argument, got {}",
651                        name,
652                        args.len()
653                    )),
654                );
655            }
656            let items = match get_value(heap, args[0]) {
657                Value::Array(items) => items.clone(),
658                _ => {
659                    return alloc_value(
660                        heap,
661                        Value::Error(format!(
662                            "builtin {} not supported for for type {}",
663                            name,
664                            value_to_string(heap, args[0])
665                        )),
666                    )
667                }
668            };
669            match builtin {
670                BuiltinId::First => items
671                    .first()
672                    .map(|item| heap.dup(*item))
673                    .unwrap_or_else(|| heap.dup(null)),
674                BuiltinId::Last => items
675                    .last()
676                    .map(|item| heap.dup(*item))
677                    .unwrap_or_else(|| heap.dup(null)),
678                BuiltinId::Rest => {
679                    if items.is_empty() {
680                        heap.dup(null)
681                    } else {
682                        alloc_value(heap, Value::Array(items[1..].to_vec()))
683                    }
684                }
685                _ => unreachable!(),
686            }
687        }
688        BuiltinId::Push => {
689            if args.len() != 2 {
690                return alloc_value(
691                    heap,
692                    Value::Error(format!("builtin push expected 2 arguments, got {}", args.len())),
693                );
694            }
695            let mut items = match get_value(heap, args[0]) {
696                Value::Array(items) => items.clone(),
697                _ => {
698                    return alloc_value(
699                        heap,
700                        Value::Error(format!(
701                            "builtin push not supported for for type {}",
702                            value_to_string(heap, args[0])
703                        )),
704                    )
705                }
706            };
707            items.push(args[1]);
708            alloc_value(heap, Value::Array(items))
709        }
710    }
711}
712
713impl fmt::Display for Value {
714    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715        write!(f, "{:?}", self)
716    }
717}