1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::vm::*;
use crate::*;

#[derive(Clone)]
pub struct CallStack(Option<Arc<StackFrame>>);

#[derive(Clone)]
pub struct StackFrame {
    pub parent: Option<Arc<StackFrame>>,
    pub receiver: Arc<Object>,
    pub method: Arc<Method>,

    pub return_address: usize,
    pub callsite: SourceCodeLocation,
}

#[derive(Clone)]
pub struct SourceCodeLocation(pub String, pub u64, pub u64);

impl CallStack {
    pub fn new() -> CallStack {
        CallStack(None)
    }

    pub fn push(
        &mut self,
        receiver: Arc<Object>,
        method: Arc<Method>,
        return_address: usize,
        callsite: SourceCodeLocation,
    ) {
        let parent = self.0.clone();
        std::mem::replace(
            self,
            CallStack(Some(Arc::new(StackFrame {
                parent,
                receiver,
                method,
                return_address,
                callsite,
            }))),
        );
    }

    pub fn ret(&mut self) -> Option<usize> {
        let frame = self.0.as_ref()?;
        let return_address = frame.return_address;
        let parent = frame.parent.clone();
        std::mem::replace(self, CallStack(parent));
        Some(return_address)
    }

    pub fn detach(&mut self) -> CallStack {
        std::mem::replace(self, CallStack::new())
    }
}

impl Into<Vec<Arc<StackFrame>>> for CallStack {
    fn into(self) -> Vec<Arc<StackFrame>> {
        let mut frame = self.0;
        let mut frames = vec![];
        while let Some(f) = frame {
            frame = f.parent.clone();
            frames.push(f);
        }
        frames
    }
}

impl fmt::Debug for CallStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for frame in self.0.iter() {
            let StackFrame {
                receiver,
                method,
                callsite: SourceCodeLocation(uri, line, character),
                ..
            } = frame.as_ref();

            writeln!(
                f,
                "{} {}\n  ({}:{}:{})",
                receiver
                    .class
                    .as_ref()
                    .map(|c| c.name.as_ref())
                    .unwrap_or("?"),
                method.name,
                uri,
                line,
                character
            )?;
        }
        Ok(())
    }
}