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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! User `Display`, `Debug` and `Drop` impls run through the VM.
use std::mem::take;
use std::sync::Arc;
use anyhow::Result;
use crate::interpreter::bytecode::Chunk;
use crate::interpreter::native::Native;
use crate::interpreter::value::{Upvalue, Value};
use crate::interpreter::vm::Vm;
use super::template::render_template;
impl Vm {
// format
pub(crate) fn render_fmt(
self: &Arc<Self>,
chunk: &Chunk,
spec: u16,
regs: &[Value],
) -> Result<String> {
let f = &chunk.fmts[spec as usize];
let positional: Vec<Value> = f
.positional
.iter()
.map(|r| regs[*r as usize].clone())
.collect();
let named: Vec<(&str, Value)> = f
.named
.iter()
.map(|(n, r)| (n.as_str(), regs[*r as usize].clone()))
.collect();
render_template(self, &f.template, &positional, &named)
}
/// None when the type has no user `Display` or `Debug` impl.
pub(crate) fn user_fmt_text(
self: &Arc<Self>,
v: &Value,
debug: bool,
) -> Result<Option<String>> {
Ok(self.user_fmt(v, debug)?.map(|(text, _)| text))
}
/// `user_fmt_text` plus whether the impl padded through `f.pad`.
pub(crate) fn user_fmt(
self: &Arc<Self>,
v: &Value,
debug: bool,
) -> Result<Option<(String, bool)>> {
let Some(methods) = self.impls.of_value(v) else {
return Ok(None);
};
let Some(chunk) = (if debug {
&methods.debug
} else {
&methods.display
})
.clone() else {
return Ok(None);
};
let handle = Arc::new(parking_lot::Mutex::new(Native::Fmt {
text: String::new(),
padded: false,
}));
let args = vec![v.clone(), Value::Native(handle.clone())];
self.run_chunk(&chunk, &args, &[], false)?;
let out = match &*handle.lock() {
Native::Fmt { text, padded } => (text.clone(), *padded),
_ => (String::new(), false),
};
Ok(Some(out))
}
/// Drops a value the current frame owns. A moved binding was cleared by its `Take`, so a
/// value that is still here is owned here. Containers hand their contents on. `Rc` and `Arc`
/// are real shared handles, the last one drops the content, so a cycle leaks like in real
/// Rust.
pub(crate) fn run_user_drop(self: &Arc<Self>, value: Value) -> Result<()> {
match value {
Value::Struct(s) => {
self.run_drop_impl(Value::Struct(s.clone()))?;
// fields drop after `Drop::drop` in declaration order
let fields = take(&mut *s.values.lock());
for field in fields {
self.run_user_drop(field)?;
}
Ok(())
}
Value::Enum { def, variant, data } => {
self.run_drop_impl(Value::Enum {
def,
variant,
data: data.clone(),
})?;
let payload = take(&mut *data.lock());
for field in payload {
self.run_user_drop(field)?;
}
Ok(())
}
Value::Vec(list) | Value::Tuple(list) => {
let items = take(&mut *list.lock());
for item in items {
self.run_user_drop(item)?;
}
Ok(())
}
Value::Map(map, _) => {
let entries = take(&mut *map.lock());
for (_, entry) in entries {
self.run_user_drop(entry)?;
}
Ok(())
}
Value::Cell(kind, slot) => {
if kind.is_shared_pointer() && Arc::strong_count(&slot) != 1 {
return Ok(());
}
let inner = take(&mut *slot.lock());
self.run_user_drop(inner)
}
Value::Native(handle) => {
let leftover = match &mut *handle.lock() {
Native::Iterator(state) => state.take_remaining(),
_ => Vec::new(),
};
for item in leftover {
self.run_user_drop(item)?;
}
Ok(())
}
// A `move` closure owns the captures it took, so they drop with it in capture
// order. A taken capture sits in its own cell, emptied here, so a second handle to
// the closure, the frame of a call still running at a panic, finds nothing left.
Value::Closure(clo) if clo.chunk.moves => {
for (captured, owned) in clo.captured.iter().zip(clo.owned.iter()) {
if !owned {
continue;
}
match captured {
Upvalue::Value(v) => self.run_user_drop(v.clone())?,
Upvalue::Mutable(cell) => {
let inner = take(&mut *cell.lock());
self.run_user_drop(inner)?;
}
}
}
Ok(())
}
_ => Ok(()),
}
}
fn run_drop_impl(self: &Arc<Self>, value: Value) -> Result<()> {
let Some(chunk) = self
.impls
.of_value(&value)
.and_then(|methods| methods.drop.clone())
else {
return Ok(());
};
self.run_chunk(&chunk, &[value], &[], false)?;
Ok(())
}
// path values
}