1use std::{
2 cell::RefMut,
3 cmp::Ordering,
4 fmt::{self, Debug},
5 hash::{Hash, Hasher},
6};
7
8use gc_arena::{lock::RefLock, Collect, Gc, Mutation};
9
10use crate::{
11 errors::{Error, ErrorKind},
12 objects::{AnyCallback, CallbackReturn, Closure, Function, IntoValue, Table, Value},
13 Context,
14};
15
16#[derive(Clone, Copy, Collect)]
17#[collect(no_drop)]
18pub struct Frames<'gc>(pub(crate) Gc<'gc, RefLock<FramesState<'gc>>>);
19
20impl<'gc> Debug for Frames<'gc> {
21 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
22 fmt.debug_tuple("Frames")
23 .field(&(&self.0 as *const _))
24 .finish()
25 }
26}
27
28impl<'gc> PartialEq for Frames<'gc> {
29 fn eq(&self, other: &Frames<'gc>) -> bool {
30 Gc::ptr_eq(self.0, other.0)
31 }
32}
33
34impl<'gc> Eq for Frames<'gc> {}
35
36impl<'gc> Hash for Frames<'gc> {
37 fn hash<H: Hasher>(&self, state: &mut H) {
38 self.0.as_ptr().hash(state)
39 }
40}
41
42#[derive(Debug, Clone, Copy, Collect, PartialEq, Eq, Hash)]
43#[collect[require_static]]
44pub enum FrameMode {
45 Stopped,
47 Normal,
49 Running,
51}
52
53impl fmt::Display for FrameMode {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "{:?}", self)
56 }
57}
58
59impl<'gc> Frames<'gc> {
60 pub fn new(mc: &Mutation<'gc>) -> Frames<'gc> {
61 Frames(Gc::new(
62 mc,
63 RefLock::new(FramesState {
64 frames: Vec::new(),
65 return_value: Value::Null,
66 }),
67 ))
68 }
69
70 pub fn mode(self) -> FrameMode {
71 if let Ok(state) = self.0.try_borrow() {
72 state.mode()
73 } else {
74 FrameMode::Running
75 }
76 }
77
78 pub fn start(
80 self,
81 ctx: Context<'gc>,
82 function: Function<'gc>,
83 args: Vec<Value<'gc>>,
84 ) -> Result<(), Error<'gc>> {
85 let mut state = self.check_mode(&ctx, FrameMode::Stopped)?;
86 state.call_function(ctx, function, args)?;
87 Ok(())
88 }
89
90 pub fn step(self, ctx: Context<'gc>) -> Result<(), Error<'gc>> {
93 let mut state = self.check_mode(&ctx, FrameMode::Normal)?;
94
95 match state.frames.last().expect("no frame to step") {
96 Frame::Callback(..) => {
97 if let Frame::Callback(callback, args) = state.frames.pop().unwrap() {
98 state.frames.push(Frame::Calling);
99
100 drop(state);
101 let return_value = callback.call(ctx, args);
102 let mut state = self.0.borrow_mut(&ctx);
103
104 assert!(
105 matches!(state.frames.pop(), Some(Frame::Calling)),
106 "thread state has changed while callback was run"
107 );
108
109 match return_value {
110 Ok(CallbackReturn::Return(v)) => match state.frames.last_mut() {
111 Some(Frame::Lua(LuciaFrame { stack, .. })) => stack.push(v),
112 _ => panic!("frame above callback must be lua frame"),
113 },
114 Ok(CallbackReturn::TailCall(f, args)) => {
115 if let Err(e) = state.call_function(ctx, f, args) {
116 state.return_error(ctx, e);
117 }
118 }
119 Err(e) => state.return_error(ctx, e),
120 }
121 }
122 }
123 Frame::Lua { .. } => {
124 const VM_GRANULARITY: u32 = 256;
125 let mut instructions = VM_GRANULARITY;
126
127 loop {
128 match state.run_vm(ctx, instructions) {
129 Ok(i) => {
130 if let Some(Frame::Lua { .. }) = state.frames.last() {
131 instructions = i;
132 if instructions == 0 {
133 break;
134 }
135 } else {
136 break;
137 }
138 }
139 Err(e) => {
140 state.return_error(ctx, e);
141 break;
142 }
143 }
144 }
145 }
146 _ => panic!("tried to step invalid frame type"),
147 }
148
149 Ok(())
150 }
151
152 fn check_mode<'a>(
153 &'a self,
154 mc: &Mutation<'gc>,
155 expected: FrameMode,
156 ) -> Result<RefMut<'a, FramesState<'gc>>, Error<'gc>> {
157 assert!(expected != FrameMode::Running);
158 let state = self.0.try_borrow_mut(mc).map_err(|_| {
159 Error::new(ErrorKind::BadFrameMode {
160 expected,
161 found: FrameMode::Running,
162 })
163 })?;
164
165 let found = state.mode();
166 if found != expected {
167 Err(Error::new(ErrorKind::BadFrameMode { expected, found }))
168 } else {
169 Ok(state)
170 }
171 }
172}
173
174#[derive(Collect)]
175#[collect(no_drop)]
176pub(crate) struct FramesState<'gc> {
177 pub frames: Vec<Frame<'gc>>,
178 pub return_value: Value<'gc>,
179}
180
181#[derive(Collect, Debug, Clone)]
182#[collect(no_drop)]
183pub struct LuciaFrame<'gc> {
184 pub pc: usize,
185 pub closure: Closure<'gc>,
186 pub locals: Vec<Value<'gc>>,
187 pub stack: Vec<Value<'gc>>,
188 pub catch_error: bool,
189}
190
191#[derive(Collect, Debug, Clone)]
192#[collect(no_drop)]
193pub enum Frame<'gc> {
194 Lua(LuciaFrame<'gc>),
196 Callback(AnyCallback<'gc>, Vec<Value<'gc>>),
198 Calling,
203}
204
205impl<'gc> LuciaFrame<'gc> {
206 pub(crate) fn new(
207 ctx: Context<'gc>,
208 closure: Closure<'gc>,
209 mut args: Vec<Value<'gc>>,
210 ) -> Result<Self, Error<'gc>> {
211 let function = &closure.0.function;
212 let params_num = function.params.len();
213 let mut stack = vec![Value::Null; params_num];
214 match args.len().cmp(¶ms_num) {
215 Ordering::Less => {
216 return Err(Error::new(ErrorKind::CallArguments {
217 value: Some(closure),
218 required: if function.variadic.is_none() {
219 params_num.into()
220 } else {
221 (params_num, None).into()
222 },
223 given: args.len(),
224 }));
225 }
226 Ordering::Equal => {
227 stack[..params_num].copy_from_slice(&args[..]);
228 if function.variadic.is_some() {
229 stack.push(Value::Table(Table::new(&ctx)));
230 }
231 }
232 Ordering::Greater => {
233 if function.variadic.is_none() {
234 return Err(Error::new(ErrorKind::CallArguments {
235 value: Some(closure),
236 required: params_num.into(),
237 given: args.len(),
238 }));
239 } else {
240 let t = args.split_off(params_num);
241 stack[..params_num].copy_from_slice(&args[..]);
242 stack.push(t.into_value(ctx));
243 }
244 }
245 }
246 Ok(LuciaFrame {
247 pc: 0,
248 closure,
249 locals: vec![Value::Null; function.local_names.len()],
250 stack,
251 catch_error: false,
252 })
253 }
254}
255
256impl<'gc> FramesState<'gc> {
257 fn mode(&self) -> FrameMode {
258 match self.frames.last() {
259 None => FrameMode::Stopped,
260 Some(frame) => match frame {
261 Frame::Lua { .. } | Frame::Callback { .. } => FrameMode::Normal,
262 Frame::Calling => FrameMode::Running,
263 },
264 }
265 }
266
267 pub(crate) fn call_function(
268 &mut self,
269 ctx: Context<'gc>,
270 function: Function<'gc>,
271 args: Vec<Value<'gc>>,
272 ) -> Result<(), Error<'gc>> {
273 self.frames.push(match function {
274 Function::Closure(closure) => Frame::Lua(LuciaFrame::new(ctx, closure, args)?),
275 Function::Callback(callback) => Frame::Callback(callback, args),
276 });
277 Ok(())
278 }
279
280 pub(crate) fn tail_call(
281 &mut self,
282 ctx: Context<'gc>,
283 function: Function<'gc>,
284 args: Vec<Value<'gc>>,
285 ) -> Result<(), Error<'gc>> {
286 *self.frames.last_mut().expect("top frame is not lua frame") = match function {
287 Function::Closure(closure) => Frame::Lua(LuciaFrame::new(ctx, closure, args)?),
288 Function::Callback(callback) => Frame::Callback(callback, args),
289 };
290 Ok(())
291 }
292
293 pub(crate) fn return_upper(&mut self) {
295 match self.frames.pop() {
296 Some(Frame::Lua(LuciaFrame { mut stack, .. })) => {
297 let return_value = stack.pop().expect("stack error");
298 match self.frames.last_mut() {
299 Some(Frame::Lua(LuciaFrame { stack, .. })) => stack.push(return_value),
300 None => self.return_value = return_value,
301 _ => panic!("lua frame must be above a lua frame"),
302 }
303 }
304 _ => panic!("top frame is not lua frame"),
305 }
306 }
307
308 pub(crate) fn return_error(&mut self, ctx: Context<'gc>, mut e: Error<'gc>) {
309 if e.traceback.is_none() {
310 e.traceback = Some(self.traceback());
311 }
312 if e.kind.recoverable() {
313 for (c, f) in self.frames.iter_mut().rev().enumerate() {
314 if let Frame::Lua(LuciaFrame {
315 catch_error: true,
316 stack,
317 ..
318 }) = f
319 {
320 stack.push(e.into_value(ctx));
321 self.frames.truncate(c);
322 return;
323 }
324 }
325 }
326 self.frames.clear();
327 self.return_value = e.into_value(ctx);
328 }
329
330 pub(crate) fn traceback(&self) -> Vec<Frame<'gc>> {
331 self.frames.clone()
332 }
333}