cao_lang/
vm.rs

1//! Cao-Lang back-end
2//!
3//! Interprets the compiled output produced by the Cao-Lang compiler
4pub(crate) mod instr_execution;
5pub mod runtime;
6
7#[cfg(test)]
8mod tests;
9
10use self::runtime::{
11    cao_lang_object::{CaoLangObjectBody, ObjectGcGuard},
12    CallFrame,
13};
14use crate::{
15    collections::handle_table::{Handle, HandleTable},
16    instruction::Instruction,
17    prelude::*,
18    stdlib,
19    value::Value,
20    vm::runtime::cao_lang_function::CaoLangClosure,
21    VariableId,
22};
23use runtime::RuntimeData;
24use std::{mem::transmute, ops::DerefMut, pin::Pin, str::FromStr};
25use tracing::debug;
26
27/// Cao-Lang bytecode interpreter.
28/// `Aux` is an auxiliary runtime structure passed to custom functions.
29pub struct Vm<'a, Aux = ()>
30where
31    Aux: 'a,
32{
33    pub auxiliary_data: Aux,
34    /// Number of instructions `run` will execute before returning Timeout
35    pub max_instr: u64,
36    pub remaining_iters: u64,
37
38    pub runtime_data: Pin<Box<RuntimeData>>,
39
40    callables: HandleTable<Procedure<Aux>>,
41    _m: std::marker::PhantomData<&'a ()>,
42}
43
44impl<'a, Aux> Vm<'a, Aux> {
45    pub fn new(auxiliary_data: Aux) -> Result<Self, ExecutionErrorPayload>
46    where
47        Aux: 'static,
48    {
49        let mut vm = Self {
50            auxiliary_data,
51            callables: HandleTable::default(),
52            runtime_data: RuntimeData::new(400 * 1024, 256, 256)?,
53            max_instr: 1000,
54            remaining_iters: 0,
55            _m: Default::default(),
56        };
57        vm.register_native_stdlib().unwrap();
58        Ok(vm)
59    }
60
61    pub fn register_native_stdlib(&mut self) -> Result<(), ExecutionErrorPayload>
62    where
63        Aux: 'static,
64    {
65        self._register_native_function("__min", into_f2(stdlib::native_minmax::<Aux, true>))?;
66        self._register_native_function("__max", into_f2(stdlib::native_minmax::<Aux, false>))?;
67        self._register_native_function("__sort", into_f2(stdlib::native_sorted::<Aux>))?;
68        self._register_native_function("__to_array", into_f1(stdlib::native_to_array::<Aux>))?;
69        Ok(())
70    }
71
72    /// Inserts the given value into the VM's runtime memory. Returns the inserted [[Value]]
73    pub fn insert_value(&mut self, value: &OwnedValue) -> Result<Value, ExecutionErrorPayload> {
74        let res = match value {
75            OwnedValue::Nil => Value::Nil,
76            OwnedValue::String(s) => {
77                let res = self.init_string(s.as_str())?;
78                Value::Object(res.0)
79            }
80            OwnedValue::Table(o) => {
81                let mut res = self.init_table()?;
82                let table = res.deref_mut().as_table_mut().unwrap();
83                for OwnedEntry { key, value } in o.iter() {
84                    let key = self.insert_value(key)?;
85                    let value = self.insert_value(value)?;
86                    table.insert(key, value)?;
87                }
88                Value::Object(res.0)
89            }
90            OwnedValue::Integer(x) => Value::Integer(*x),
91            OwnedValue::Real(x) => Value::Real(*x),
92        };
93        Ok(res)
94    }
95
96    pub fn init_native_function(
97        &mut self,
98        handle: Handle,
99    ) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
100        self.runtime_data.init_native_function(handle)
101    }
102
103    pub fn init_function(
104        &mut self,
105        handle: Handle,
106        arity: u32,
107    ) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
108        self.runtime_data.init_function(handle, arity)
109    }
110
111    pub fn init_closure(
112        &mut self,
113        handle: Handle,
114        arity: u32,
115    ) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
116        self.runtime_data.init_closure(handle, arity)
117    }
118
119    pub fn init_upvalue(
120        &mut self,
121        location: *mut Value,
122    ) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
123        self.runtime_data.init_upvalue(location)
124    }
125
126    pub fn clear(&mut self) {
127        self.runtime_data.clear();
128    }
129
130    pub fn read_var_by_name(&self, name: &str, vars: &Variables) -> Option<Value> {
131        let varid = vars.ids.get(Handle::from_str(name).ok()?)?;
132        self.read_var(*varid)
133    }
134
135    #[inline]
136    pub fn read_var(&self, name: VariableId) -> Option<Value> {
137        self.runtime_data.global_vars.get(name.0 as usize).cloned()
138    }
139
140    #[must_use]
141    pub fn with_max_iter(mut self, max_iter: u64) -> Self {
142        self.max_instr = max_iter;
143        self
144    }
145
146    #[inline]
147    pub fn get_aux(&self) -> &Aux {
148        &self.auxiliary_data
149    }
150
151    #[inline]
152    pub fn get_aux_mut(&mut self) -> &mut Aux {
153        &mut self.auxiliary_data
154    }
155
156    #[inline]
157    pub fn unwrap_aux(self) -> Aux {
158        self.auxiliary_data
159    }
160
161    /// Register a native function for use by Cao-Lang programs
162    ///
163    pub fn register_native_function<S, C>(
164        &mut self,
165        name: S,
166        f: C,
167    ) -> Result<(), ExecutionErrorPayload>
168    where
169        S: AsRef<str>,
170        C: VmFunction<Aux> + 'static,
171    {
172        if name.as_ref().starts_with("__") {
173            return Err(ExecutionErrorPayload::invalid_argument(
174                "Native function name may not begin with __",
175            ));
176        }
177        self._register_native_function(name, f)
178    }
179
180    fn _register_native_function<S, C>(
181        &mut self,
182        name: S,
183        f: C,
184    ) -> Result<(), ExecutionErrorPayload>
185    where
186        S: AsRef<str>,
187        C: VmFunction<Aux> + 'static,
188    {
189        let key = Handle::from_str(name.as_ref()).unwrap();
190        self.callables
191            .insert(
192                key,
193                Procedure {
194                    name: name.as_ref().to_owned(),
195                    fun: std::rc::Rc::new(f),
196                },
197            )
198            .map_err(|_| ExecutionErrorPayload::OutOfMemory)
199            .map(drop)
200    }
201
202    #[inline]
203    pub fn stack_push<S>(&mut self, value: S) -> Result<(), ExecutionErrorPayload>
204    where
205        S: Into<Value>,
206    {
207        self.runtime_data
208            .value_stack
209            .push(value.into())
210            .map_err(|_| ExecutionErrorPayload::Stackoverflow)?;
211        Ok(())
212    }
213
214    #[inline]
215    pub fn stack_pop(&mut self) -> Value {
216        self.runtime_data.value_stack.pop()
217    }
218
219    pub fn get_table(&self, value: Value) -> Result<&CaoLangTable, ExecutionErrorPayload> {
220        let res = match value {
221            Value::Object(o) => unsafe {
222                o.as_ref()
223                    .as_table()
224                    .ok_or_else(|| ExecutionErrorPayload::invalid_argument("Expected Table"))?
225            },
226            _ => {
227                debug!("Got {:?} instead of object", value);
228                return Err(ExecutionErrorPayload::invalid_argument("Expected Table"));
229            }
230        };
231        Ok(res)
232    }
233
234    pub fn get_table_mut(&self, value: Value) -> Result<&mut CaoLangTable, ExecutionErrorPayload> {
235        let res = match value {
236            Value::Object(mut o) => unsafe {
237                o.as_mut()
238                    .as_table_mut()
239                    .ok_or_else(|| ExecutionErrorPayload::invalid_argument("Expected Table"))?
240            },
241            _ => {
242                debug!("Got {:?} instead of object", value);
243                return Err(ExecutionErrorPayload::invalid_argument("Expected Table"));
244            }
245        };
246        Ok(res)
247    }
248
249    /// Initializes a new FieldTable in this VM instance
250    #[inline]
251    pub fn init_table(&mut self) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
252        self.runtime_data.init_table()
253    }
254
255    /// Initializes a new string owned by this VM instance
256    pub fn init_string(&mut self, payload: &str) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
257        self.runtime_data.init_string(payload)
258    }
259
260    /// Panics if no current program has been set
261    pub fn run_function(&mut self, val: Value) -> Result<Value, ExecutionErrorPayload> {
262        let Value::Object(obj) = val else {
263            return Err(ExecutionErrorPayload::invalid_argument(
264                "Expected a function object argument",
265            ));
266        };
267        let arity;
268        let label;
269        let mut closure: *mut CaoLangClosure = std::ptr::null_mut();
270        unsafe {
271            match &obj.as_ref().body {
272                CaoLangObjectBody::Closure(c) => {
273                    arity = c.function.arity;
274                    label = c.function.handle;
275                    closure = (c as *const CaoLangClosure).cast_mut();
276                }
277                CaoLangObjectBody::Function(f) => {
278                    arity = f.arity;
279                    label = f.handle;
280                }
281                CaoLangObjectBody::NativeFunction(f) => {
282                    instr_execution::call_native(self, f.handle)?;
283                    return Ok(self.stack_pop());
284                }
285                _ => {
286                    return Err(ExecutionErrorPayload::invalid_argument(format!(
287                        "Expected a function object argument, instead got: {}",
288                        obj.as_ref().type_name()
289                    )));
290                }
291            }
292        }
293        let program: &CaoCompiledProgram = unsafe {
294            let program = self.runtime_data.current_program;
295            assert!(!program.is_null());
296            &*program
297        };
298        debug_assert!(!program.bytecode.is_empty());
299
300        let func = program
301            .labels
302            .0
303            .get(label)
304            .ok_or_else(|| ExecutionErrorPayload::ProcedureNotFound(label))?;
305
306        let src = func.pos;
307        let end = program.bytecode.len() - 1;
308        let len = self.runtime_data.value_stack.len() as u32;
309
310        // a function call needs 2 stack frames, 1 for the current scope, another for the return
311        // address
312        //
313        // the first one will be used as a trap, to exit the program,
314        // the second one is the actual callframe of the function
315        for _ in 0..2 {
316            self.runtime_data
317                .call_stack
318                .push(CallFrame {
319                    src_instr_ptr: src,
320                    dst_instr_ptr: end as u32,
321                    stack_offset: len
322                        .checked_sub(arity)
323                        .ok_or(ExecutionErrorPayload::MissingArgument)?,
324                    closure,
325                })
326                .map_err(|_| ExecutionErrorPayload::CallStackOverflow)?;
327        }
328
329        let mut instr_ptr = src as usize;
330        self._run(&mut instr_ptr).map_err(|err| err.payload)?;
331        // pop the trap callframe
332        self.runtime_data.call_stack.pop();
333        Ok(self.stack_pop())
334    }
335
336    fn _run(&mut self, instr_ptr: &mut usize) -> ExecutionResult<()> {
337        let program: &CaoCompiledProgram = unsafe {
338            let program = self.runtime_data.current_program;
339            assert!(!program.is_null());
340            &*program
341        };
342        let len = program.bytecode.len();
343        // FIXME: should store in VM
344        let mut remaining_iters = self.max_instr;
345        let bytecode_ptr = program.bytecode.as_ptr();
346        let payload_to_error =
347            |err,
348             instr_ptr: usize,
349             stack: &crate::collections::bounded_stack::BoundedStack<CallFrame>| {
350                let mut trace = Vec::with_capacity(stack.len() + 1);
351                if let Some(t) = program.trace.get(&(instr_ptr as u32)).cloned() {
352                    trace.push(t);
353                }
354                for t in stack.iter_backwards() {
355                    if let Some(t) = program.trace.get(&t.src_instr_ptr) {
356                        trace.push(t.clone())
357                    }
358                }
359                ExecutionError::new(err, trace)
360            };
361
362        while *instr_ptr < len {
363            remaining_iters -= 1;
364            if remaining_iters == 0 {
365                return Err(payload_to_error(
366                    ExecutionErrorPayload::Timeout,
367                    *instr_ptr,
368                    &self.runtime_data.call_stack,
369                ));
370            }
371            let instr: u8 = unsafe { *bytecode_ptr.add(*instr_ptr) };
372            let instr: Instruction = unsafe { transmute(instr) };
373            let src_ptr = *instr_ptr;
374            *instr_ptr += 1;
375            debug!("Executing: {instr:?} instr_ptr: {instr_ptr}");
376            match instr {
377                Instruction::InitTable => {
378                    let res = self.init_table().map_err(|err| {
379                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
380                    })?;
381                    self.stack_push(Value::Object(res.0)).map_err(|err| {
382                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
383                    })?;
384                }
385                Instruction::GetProperty => {
386                    let key = self.stack_pop();
387                    let instance = self.stack_pop();
388                    let table = self.get_table(instance).map_err(|err| {
389                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
390                    })?;
391                    let result = table.get(&key).copied().unwrap_or(Value::Nil);
392                    self.stack_push(result).map_err(|err| {
393                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
394                    })?;
395                }
396                Instruction::SetProperty => {
397                    let [key, instance, value] = self.runtime_data.value_stack.pop_n::<3>();
398                    let table = self.get_table_mut(instance).map_err(|err| {
399                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
400                    })?;
401                    table
402                        .insert(key, value)
403                        .map_err(|err| {
404                            debug!("Failed to insert value {:?}", err);
405                            ExecutionErrorPayload::OutOfMemory
406                        })
407                        .map_err(|err| {
408                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
409                        })?;
410                }
411                Instruction::BeginForEach => {
412                    instr_execution::begin_for_each(self, &program.bytecode, instr_ptr).map_err(
413                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
414                    )?;
415                }
416                Instruction::ForEach => {
417                    instr_execution::for_each(self, &program.bytecode, instr_ptr).map_err(
418                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
419                    )?;
420                }
421                Instruction::GotoIfTrue => {
422                    let condition = self.runtime_data.value_stack.pop();
423                    let pos: i32 =
424                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
425                    debug_assert!(pos >= 0);
426                    if condition.as_bool() {
427                        *instr_ptr = pos as usize;
428                    }
429                }
430                Instruction::GotoIfFalse => {
431                    let condition = self.runtime_data.value_stack.pop();
432                    let pos: i32 =
433                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
434                    debug_assert!(pos >= 0);
435                    if !condition.as_bool() {
436                        *instr_ptr = pos as usize;
437                    }
438                }
439                Instruction::Goto => {
440                    let pos: i32 =
441                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
442                    debug_assert!(pos >= 0);
443                    *instr_ptr = pos as usize;
444                }
445                Instruction::SwapLast => {
446                    let b = self.stack_pop();
447                    let a = self.stack_pop();
448                    // we popped two values, we know that the stack has capacity for 2 ..
449                    self.stack_push(b).unwrap();
450                    self.stack_push(a).unwrap();
451                }
452                Instruction::ScalarNil => self.stack_push(Value::Nil).map_err(|err| {
453                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
454                })?,
455                Instruction::ClearStack => {
456                    let offset = self
457                        .runtime_data
458                        .call_stack
459                        .last()
460                        .expect("No callframe available")
461                        .stack_offset as usize;
462                    self.runtime_data.value_stack.clear_until(offset);
463                }
464                Instruction::SetLocalVar => {
465                    instr_execution::set_local(self, &program.bytecode, instr_ptr).map_err(
466                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
467                    )?;
468                }
469                Instruction::ReadLocalVar => {
470                    instr_execution::get_local(self, &program.bytecode, instr_ptr).map_err(
471                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
472                    )?;
473                }
474                Instruction::SetGlobalVar => {
475                    instr_execution::instr_set_var(
476                        &mut self.runtime_data,
477                        &program.bytecode,
478                        instr_ptr,
479                    )
480                    .map_err(|err| {
481                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
482                    })?;
483                }
484                Instruction::ReadGlobalVar => {
485                    instr_execution::instr_read_var(&mut self.runtime_data, instr_ptr, program)
486                        .map_err(|err| {
487                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
488                        })?;
489                }
490                Instruction::Pop => {
491                    self.stack_pop();
492                }
493                Instruction::CallFunction => {
494                    instr_execution::instr_call_function(src_ptr, instr_ptr, program, self)
495                        .map_err(|err| {
496                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
497                        })?;
498                }
499                Instruction::Return => {
500                    instr_execution::instr_return(self, instr_ptr).map_err(|err| {
501                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
502                    })?;
503                }
504                Instruction::Exit => return Ok(()),
505                Instruction::CopyLast => {
506                    instr_execution::instr_copy_last(self).map_err(|err| {
507                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
508                    })?;
509                }
510                Instruction::NativeFunctionPointer => {
511                    let handle: u32 =
512                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
513                    let fun_name =
514                        instr_execution::read_str(&mut (handle as usize), program.data.as_slice())
515                            .ok_or(ExecutionErrorPayload::InvalidArgument { context: None })
516                            .map_err(|err| {
517                                payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
518                            })?;
519                    let handle = Handle::from_str(fun_name).unwrap();
520                    let obj = self.init_native_function(handle).map_err(|err| {
521                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
522                    })?;
523                    let val = Value::Object(obj.0);
524                    self.runtime_data
525                        .value_stack
526                        .push(val)
527                        .map_err(|_| ExecutionErrorPayload::Stackoverflow)
528                        .map_err(|err| {
529                            // free the object on Stackoverflow
530                            self.runtime_data.free_object(obj.0);
531                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
532                        })?;
533                }
534                Instruction::FunctionPointer => {
535                    let hash: Handle =
536                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
537                    let arity: u32 =
538                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
539
540                    let obj = self.init_function(hash, arity).map_err(|err| {
541                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
542                    })?;
543
544                    let val = Value::Object(obj.0);
545
546                    self.runtime_data
547                        .value_stack
548                        .push(val)
549                        .map_err(|_| ExecutionErrorPayload::Stackoverflow)
550                        .map_err(|err| {
551                            // free the object on Stackoverflow
552                            self.runtime_data.free_object(obj.0);
553                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
554                        })?;
555                }
556                Instruction::Closure => {
557                    let hash: Handle =
558                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
559                    let arity: u32 =
560                        unsafe { instr_execution::decode_value(&program.bytecode, instr_ptr) };
561
562                    let obj = self.init_closure(hash, arity).map_err(|err| {
563                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
564                    })?;
565
566                    let val = Value::Object(obj.0);
567
568                    self.runtime_data
569                        .value_stack
570                        .push(val)
571                        .map_err(|_| ExecutionErrorPayload::Stackoverflow)
572                        .map_err(|err| {
573                            // free the object on Stackoverflow
574                            self.runtime_data.free_object(obj.0);
575                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
576                        })?;
577                }
578                Instruction::ScalarInt => {
579                    self.runtime_data
580                        .value_stack
581                        .push(Value::Integer(unsafe {
582                            instr_execution::decode_value(&program.bytecode, instr_ptr)
583                        }))
584                        .map_err(|_| ExecutionErrorPayload::Stackoverflow)
585                        .map_err(|err| {
586                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
587                        })?;
588                }
589                Instruction::ScalarFloat => {
590                    self.runtime_data
591                        .value_stack
592                        .push(Value::Real(unsafe {
593                            instr_execution::decode_value(&program.bytecode, instr_ptr)
594                        }))
595                        .map_err(|_| ExecutionErrorPayload::Stackoverflow)
596                        .map_err(|err| {
597                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
598                        })?;
599                }
600                Instruction::Not => {
601                    let value = self.stack_pop();
602                    let value = !value.as_bool();
603                    self.stack_push(Value::Integer(value as i64))
604                        .map_err(|err| {
605                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
606                        })?;
607                }
608                Instruction::And => self
609                    .binary_op(|a, b| Value::from(a.as_bool() && b.as_bool()))
610                    .map_err(|err| {
611                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
612                    })?,
613                Instruction::Or => self
614                    .binary_op(|a, b| Value::from(a.as_bool() || b.as_bool()))
615                    .map_err(|err| {
616                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
617                    })?,
618                Instruction::Xor => self
619                    .binary_op(|a, b| Value::from(a.as_bool() ^ b.as_bool()))
620                    .map_err(|err| {
621                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
622                    })?,
623                Instruction::Add => self.binary_op(|a, b| a + b).map_err(|err| {
624                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
625                })?,
626                Instruction::Sub => self.binary_op(|a, b| a - b).map_err(|err| {
627                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
628                })?,
629                Instruction::Mul => self.binary_op(|a, b| a * b).map_err(|err| {
630                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
631                })?,
632                Instruction::Div => self.binary_op(|a, b| a / b).map_err(|err| {
633                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
634                })?,
635                Instruction::Equals => self.binary_op(|a, b| (a == b).into()).map_err(|err| {
636                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
637                })?,
638                Instruction::NotEquals => {
639                    self.binary_op(|a, b| (a != b).into()).map_err(|err| {
640                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
641                    })?
642                }
643                Instruction::Less => self.binary_op(|a, b| (a < b).into()).map_err(|err| {
644                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
645                })?,
646                Instruction::LessOrEq => self.binary_op(|a, b| (a <= b).into()).map_err(|err| {
647                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
648                })?,
649                Instruction::StringLiteral => instr_execution::instr_string_literal(
650                    self, instr_ptr, program,
651                )
652                .map_err(|err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack))?,
653                Instruction::CallNative => {
654                    instr_execution::execute_call_native(self, instr_ptr, &program.bytecode)
655                        .map_err(|err| {
656                            payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
657                        })?
658                }
659                Instruction::Len => instr_execution::instr_len(self).map_err(|err| {
660                    payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
661                })?,
662                Instruction::NthRow => {
663                    let [i, instance] = self.runtime_data.value_stack.pop_n::<2>();
664                    let table = self.get_table_mut(instance).map_err(|err| {
665                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
666                    })?;
667                    let i = i.as_int().ok_or_else(|| {
668                        payload_to_error(
669                            ExecutionErrorPayload::invalid_argument(
670                                "Input must be an integer".to_string(),
671                            ),
672                            *instr_ptr,
673                            &self.runtime_data.call_stack,
674                        )
675                    })?;
676                    if i < 0 {
677                        return Err(payload_to_error(
678                            ExecutionErrorPayload::invalid_argument(
679                                "Input must be non-negative".to_string(),
680                            ),
681                            *instr_ptr,
682                            &self.runtime_data.call_stack,
683                        ));
684                    }
685                    let key = table.nth_key(i as usize);
686                    let value = table.get(&key).copied().unwrap_or(Value::Nil);
687
688                    debug!(
689                        i = i,
690                        key = tracing::field::debug(key),
691                        value = tracing::field::debug(value),
692                        table = tracing::field::debug(instance),
693                        "Getting row of table"
694                    );
695
696                    (|| {
697                        let mut row = self.init_table()?;
698                        let row_table = row.as_table_mut().unwrap();
699                        let k = self.init_string("key")?;
700                        let v = self.init_string("value")?;
701                        row_table.insert(Value::Object(k.0), key)?;
702                        row_table.insert(Value::Object(v.0), value)?;
703                        self.stack_push(Value::Object(row.0))?;
704                        Ok(())
705                    })()
706                    .map_err(|err| {
707                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
708                    })?;
709                }
710                Instruction::AppendTable => {
711                    let instance = self.stack_pop();
712                    let value = self.stack_pop();
713                    let table = self.get_table_mut(instance).map_err(|err| {
714                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
715                    })?;
716                    table.append(value).map_err(|err| {
717                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
718                    })?;
719                }
720
721                Instruction::PopTable => {
722                    let instance = self.stack_pop();
723                    let table = self.get_table_mut(instance).map_err(|err| {
724                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
725                    })?;
726                    let value = table.pop().map_err(|err| {
727                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
728                    })?;
729                    self.stack_push(value).map_err(|err| {
730                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
731                    })?;
732                }
733                Instruction::SetUpvalue => {
734                    instr_execution::write_upvalue(self, &program.bytecode, instr_ptr).map_err(
735                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
736                    )?;
737                }
738                Instruction::ReadUpvalue => {
739                    instr_execution::read_upvalue(self, &program.bytecode, instr_ptr).map_err(
740                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
741                    )?;
742                }
743                Instruction::RegisterUpvalue => {
744                    instr_execution::register_upvalue(self, &program.bytecode, instr_ptr).map_err(
745                        |err| payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack),
746                    )?;
747                }
748                Instruction::CloseUpvalue => {
749                    instr_execution::close_upvalues(self).map_err(|err| {
750                        payload_to_error(err, *instr_ptr, &self.runtime_data.call_stack)
751                    })?;
752                }
753            }
754            debug!("Stack: {}", self.runtime_data.value_stack);
755        }
756
757        Err(payload_to_error(
758            ExecutionErrorPayload::UnexpectedEndOfInput,
759            *instr_ptr,
760            &self.runtime_data.call_stack,
761        ))
762    }
763
764    /// This mostly assumes that program is valid, produced by the compiler.
765    /// As such running non-compiler emitted programs is very un-safe
766    pub fn run(&mut self, program: &CaoCompiledProgram) -> ExecutionResult<()> {
767        self.runtime_data.current_program = program as *const _;
768        self.runtime_data
769            .call_stack
770            .push(CallFrame {
771                src_instr_ptr: 0,
772                dst_instr_ptr: 0,
773                stack_offset: 0,
774                closure: std::ptr::null_mut(),
775            })
776            .map_err(|_| ExecutionErrorPayload::CallStackOverflow)
777            .map_err(|pl| ExecutionError::new(pl, Default::default()))?;
778
779        self.remaining_iters = self.max_instr;
780        let mut instr_ptr = 0;
781        let result = self._run(&mut instr_ptr);
782        self.runtime_data.current_program = std::ptr::null();
783        result
784    }
785
786    #[inline]
787    fn binary_op(&mut self, op: fn(Value, Value) -> Value) -> Result<(), ExecutionErrorPayload> {
788        let b = self.stack_pop();
789        let a = self.stack_pop();
790
791        self.runtime_data
792            .value_stack
793            .push(op(a, b))
794            .map_err(|_| ExecutionErrorPayload::Stackoverflow)?;
795        Ok(())
796    }
797}