1use crate::value::LuaValue;
4use crate::StackIdx;
5use std::cell::Cell;
6
7#[derive(Debug)]
27pub struct UpVal {
28 open_thread_id: Cell<i64>,
29 open_idx: Cell<u32>,
30 closed_value: Cell<LuaValue>,
31}
32
33const CLOSED_TAG: i64 = -1;
37
38impl UpVal {
39 pub fn open(thread_id: usize, idx: StackIdx) -> Self {
40 UpVal {
41 open_thread_id: Cell::new(thread_id as i64),
42 open_idx: Cell::new(idx.0),
43 closed_value: Cell::new(LuaValue::Nil),
44 }
45 }
46
47 pub fn closed(v: LuaValue) -> Self {
48 UpVal {
49 open_thread_id: Cell::new(CLOSED_TAG),
50 open_idx: Cell::new(0),
51 closed_value: Cell::new(v),
52 }
53 }
54
55 pub fn is_open(&self) -> bool {
56 self.open_thread_id.get() >= 0
57 }
58 pub fn is_closed(&self) -> bool {
59 self.open_thread_id.get() < 0
60 }
61
62 #[inline(always)]
67 pub fn try_open_payload(&self) -> Option<(usize, StackIdx)> {
68 let tid = self.open_thread_id.get();
69 if tid < 0 {
70 None
71 } else {
72 Some((tid as usize, StackIdx(self.open_idx.get())))
73 }
74 }
75
76 #[inline(always)]
79 pub fn closed_value(&self) -> LuaValue {
80 self.closed_value.get()
81 }
82
83 pub fn close_with(&self, v: LuaValue) {
84 self.open_thread_id.set(CLOSED_TAG);
85 self.open_idx.set(0);
86 self.closed_value.set(v);
87 }
88
89 pub fn set_closed_value(&self, v: LuaValue) {
90 self.open_thread_id.set(CLOSED_TAG);
91 self.open_idx.set(0);
92 self.closed_value.set(v);
93 }
94
95 pub fn try_closed_value(&self) -> Option<LuaValue> {
96 if self.is_closed() {
97 Some(self.closed_value.get())
98 } else {
99 None
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn closed_scalar_write_updates_canonical_value() {
110 let uv = UpVal::closed(LuaValue::Int(1));
111
112 uv.set_closed_value(LuaValue::Int(2));
113
114 assert_eq!(uv.closed_value(), LuaValue::Int(2));
115 assert_eq!(uv.try_closed_value(), Some(LuaValue::Int(2)));
116 assert!(uv.is_closed());
117 assert_eq!(uv.try_open_payload(), None);
118 }
119
120 #[test]
121 fn close_with_sets_cell_closed_state() {
122 let uv = UpVal::open(7, StackIdx(3));
123 assert_eq!(uv.try_open_payload(), Some((7, StackIdx(3))));
124
125 uv.close_with(LuaValue::Bool(true));
126
127 assert_eq!(uv.closed_value(), LuaValue::Bool(true));
128 assert_eq!(uv.try_closed_value(), Some(LuaValue::Bool(true)));
129 assert!(uv.is_closed());
130 assert_eq!(uv.try_open_payload(), None);
131 }
132}