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