1use std::rc::Rc;
18use std::sync::{mpsc, Arc, Mutex};
19
20use crate::error::{DogeError, DogeResult, ErrorKind, ErrorLocation};
21use crate::ordered_map::OrderedMap;
22use crate::value::{ObjectData, SocketData, SocketState, Value};
23
24const PACK_DEPTH_LIMIT: usize = 500;
30
31#[derive(Clone, Copy, PartialEq, Eq)]
33pub enum PackMode {
34 Transfer,
37 Snapshot,
40}
41
42#[derive(Debug)]
45pub enum Packed {
46 Int(i64),
47 Float(f64),
48 Str(String),
49 Bool(bool),
50 None,
51 List(Vec<Packed>),
52 Dict(Vec<(String, Packed)>),
53 Object {
54 class_id: u32,
55 class_name: String,
56 fields: Vec<(String, Packed)>,
57 },
58 Function {
59 fn_id: u32,
60 name: String,
61 captures: Vec<Packed>,
62 },
63 Class {
64 fn_id: u32,
65 name: String,
66 },
67 BoundMethod {
68 receiver: Box<Packed>,
69 method: String,
70 },
71 Error {
72 kind: ErrorKind,
73 message: String,
74 file: String,
75 line: u32,
76 },
77 Socket(SocketState),
78 Bowl(BowlHandle),
79}
80
81#[derive(Clone, Debug)]
86pub struct BowlHandle {
87 pub(crate) sender: mpsc::Sender<Packed>,
88 pub(crate) receiver: Arc<Mutex<mpsc::Receiver<Packed>>>,
89}
90
91impl BowlHandle {
92 pub fn new() -> BowlHandle {
94 let (sender, receiver) = mpsc::channel();
95 BowlHandle {
96 sender,
97 receiver: Arc::new(Mutex::new(receiver)),
98 }
99 }
100}
101
102impl Default for BowlHandle {
103 fn default() -> Self {
104 BowlHandle::new()
105 }
106}
107
108#[derive(Debug)]
112pub struct PackedError {
113 kind: ErrorKind,
114 message: String,
115 location: Option<(String, u32)>,
116}
117
118impl PackedError {
119 pub fn from_error(err: DogeError) -> PackedError {
121 PackedError {
122 kind: err.kind,
123 message: err.message,
124 location: err.location.map(|loc| (loc.file.to_string(), loc.line)),
125 }
126 }
127
128 pub fn into_error(self) -> DogeError {
131 DogeError {
132 kind: self.kind,
133 message: self.message,
134 location: self.location.map(|(file, line)| ErrorLocation {
135 file: Rc::from(file),
136 line,
137 }),
138 }
139 }
140}
141
142pub type PupEntry = fn(Packed, Packed, Vec<Packed>) -> Result<Packed, PackedError>;
147
148pub fn pack_value(value: &Value, mode: PackMode) -> DogeResult<Packed> {
153 pack_at(value, mode, 0)
154}
155
156pub fn pack_snapshot(value: &Value) -> Packed {
161 pack_value(value, PackMode::Snapshot).unwrap_or(Packed::None)
162}
163
164fn pack_at(value: &Value, mode: PackMode, depth: usize) -> DogeResult<Packed> {
165 if depth >= PACK_DEPTH_LIMIT {
166 return Err(DogeError::value_error(
167 "cannot send a value nested that deeply (or referring to itself) to a pup",
168 ));
169 }
170 let next = depth + 1;
171 Ok(match value {
172 Value::Int(n) => Packed::Int(*n),
173 Value::Float(f) => Packed::Float(*f),
174 Value::Str(s) => Packed::Str(s.to_string()),
175 Value::Bool(b) => Packed::Bool(*b),
176 Value::None => Packed::None,
177 Value::List(items) => {
178 let mut out = Vec::with_capacity(items.borrow().len());
179 for item in items.borrow().iter() {
180 out.push(pack_at(item, mode, next)?);
181 }
182 Packed::List(out)
183 }
184 Value::Dict(entries) => {
185 let entries = entries.borrow();
186 let mut out = Vec::with_capacity(entries.len());
187 for (key, val) in entries.iter() {
188 out.push((key.to_string(), pack_at(val, mode, next)?));
189 }
190 Packed::Dict(out)
191 }
192 Value::Object(obj) => {
193 let obj = obj.borrow();
194 let mut fields = Vec::with_capacity(obj.fields.len());
195 for (name, val) in obj.fields.iter() {
196 fields.push((name.clone(), pack_at(val, mode, next)?));
197 }
198 Packed::Object {
199 class_id: obj.class_id,
200 class_name: obj.class_name.to_string(),
201 fields,
202 }
203 }
204 Value::Function(func) => {
205 let mut captures = Vec::with_capacity(func.captures.len());
206 for cell in func.captures.iter() {
207 captures.push(pack_at(&cell.borrow(), mode, next)?);
208 }
209 Packed::Function {
210 fn_id: func.fn_id,
211 name: func.name.to_string(),
212 captures,
213 }
214 }
215 Value::Class(class) => Packed::Class {
216 fn_id: class.fn_id,
217 name: class.name.to_string(),
218 },
219 Value::BoundMethod(method) => Packed::BoundMethod {
220 receiver: Box::new(pack_at(&method.receiver, mode, next)?),
221 method: method.method.to_string(),
222 },
223 Value::Error(err) => Packed::Error {
224 kind: err.kind,
225 message: err.message.to_string(),
226 file: err.file.to_string(),
227 line: err.line,
228 },
229 Value::Socket(socket) => Packed::Socket(pack_socket(socket, mode)),
230 Value::Bowl(bowl) => Packed::Bowl(bowl.handle.clone()),
231 Value::Pup(_) => {
232 return Err(DogeError::type_error("cannot send a Pup to another pup"));
233 }
234 })
235}
236
237fn pack_socket(socket: &Rc<SocketData>, mode: PackMode) -> SocketState {
240 match mode {
241 PackMode::Transfer => socket.state.replace(SocketState::Closed),
242 PackMode::Snapshot => SocketState::Closed,
243 }
244}
245
246pub fn unpack_packed(packed: Packed) -> Value {
250 match packed {
251 Packed::Int(n) => Value::Int(n),
252 Packed::Float(f) => Value::Float(f),
253 Packed::Str(s) => Value::str(s),
254 Packed::Bool(b) => Value::Bool(b),
255 Packed::None => Value::None,
256 Packed::List(items) => Value::list(items.into_iter().map(unpack_packed).collect()),
257 Packed::Dict(pairs) => {
258 let mut entries = OrderedMap::new();
259 for (key, val) in pairs {
260 entries.insert(key, unpack_packed(val));
261 }
262 Value::dict(entries)
263 }
264 Packed::Object {
265 class_id,
266 class_name,
267 fields,
268 } => {
269 let fields = fields
270 .into_iter()
271 .map(|(name, val)| (name, unpack_packed(val)))
272 .collect();
273 Value::Object(Rc::new(std::cell::RefCell::new(ObjectData {
274 class_id,
275 class_name: Rc::from(class_name.as_str()),
276 fields,
277 })))
278 }
279 Packed::Function {
280 fn_id,
281 name,
282 captures,
283 } => {
284 let cells = captures
285 .into_iter()
286 .map(|c| Rc::new(std::cell::RefCell::new(unpack_packed(c))))
287 .collect();
288 Value::function(fn_id, &name, cells)
289 }
290 Packed::Class { fn_id, name } => Value::class(fn_id, &name),
291 Packed::BoundMethod { receiver, method } => {
292 Value::bound_method(unpack_packed(*receiver), &method)
293 }
294 Packed::Error {
295 kind,
296 message,
297 file,
298 line,
299 } => Value::error(kind, &message, Rc::from(file.as_str()), line),
300 Packed::Socket(state) => Value::socket(state),
301 Packed::Bowl(handle) => Value::bowl(handle),
302 }
303}
304
305pub fn unpack_globals(globals: Packed) -> Vec<Value> {
309 match globals {
310 Packed::List(items) => items.into_iter().map(unpack_packed).collect(),
311 _ => Vec::new(),
312 }
313}
314
315pub fn finish_pup(result: DogeResult<Value>) -> Result<Packed, PackedError> {
320 match result {
321 Ok(value) => pack_value(&value, PackMode::Transfer).map_err(PackedError::from_error),
322 Err(err) => Err(PackedError::from_error(err)),
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 fn round_trip(value: &Value) -> Value {
331 unpack_packed(pack_value(value, PackMode::Transfer).unwrap())
332 }
333
334 #[test]
335 fn scalars_and_collections_survive_a_round_trip() {
336 let mut map = OrderedMap::new();
337 map.insert("k".to_string(), Value::Int(1));
338 let value = Value::list(vec![
339 Value::Int(7),
340 Value::Float(2.5),
341 Value::str("wow"),
342 Value::Bool(true),
343 Value::None,
344 Value::dict(map),
345 ]);
346 assert!(crate::values_equal(&value, &round_trip(&value)));
347 }
348
349 #[test]
350 fn a_copy_shares_nothing_with_the_original() {
351 let inner = Value::list(vec![Value::Int(1)]);
352 let copy = round_trip(&inner);
353 if let Value::List(items) = © {
355 items.borrow_mut().push(Value::Int(2));
356 }
357 let Value::List(original) = &inner else {
358 unreachable!()
359 };
360 assert_eq!(original.borrow().len(), 1);
361 }
362
363 #[test]
364 fn a_pup_cannot_be_packed() {
365 let err = pack_value(&fake_pup(), PackMode::Transfer).unwrap_err();
368 assert_eq!(err.kind, ErrorKind::TypeError);
369 }
370
371 fn fake_pup() -> Value {
372 let handle = std::thread::spawn(|| Ok(Packed::None));
374 Value::pup(handle)
375 }
376
377 #[test]
378 fn a_deeply_nested_value_is_a_catchable_error_not_a_hang() {
379 let handle = std::thread::Builder::new()
383 .stack_size(64 * 1024 * 1024)
384 .spawn(|| {
385 let list = Value::list(vec![]);
386 if let Value::List(items) = &list {
387 items.borrow_mut().push(list.clone());
388 }
389 pack_value(&list, PackMode::Transfer).unwrap_err().kind
390 })
391 .unwrap();
392 assert_eq!(handle.join().unwrap(), ErrorKind::ValueError);
393 }
394
395 #[test]
396 fn transfer_moves_a_socket_but_snapshot_leaves_it_open() {
397 let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("binds a loopback port");
398 let socket = Value::socket(SocketState::Listener(listener));
399
400 let _ = pack_value(&socket, PackMode::Snapshot).unwrap();
402 let Value::Socket(handle) = &socket else {
403 unreachable!()
404 };
405 assert!(
406 matches!(&*handle.state.borrow(), SocketState::Listener(_)),
407 "snapshot leaves the original open"
408 );
409
410 let _ = pack_value(&socket, PackMode::Transfer).unwrap();
412 assert!(
413 matches!(&*handle.state.borrow(), SocketState::Closed),
414 "transfer closes the original"
415 );
416 }
417}