1pub(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
27pub struct Vm<'a, Aux = ()>
30where
31 Aux: 'a,
32{
33 pub auxiliary_data: Aux,
34 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 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 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 #[inline]
251 pub fn init_table(&mut self) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
252 self.runtime_data.init_table()
253 }
254
255 pub fn init_string(&mut self, payload: &str) -> Result<ObjectGcGuard, ExecutionErrorPayload> {
257 self.runtime_data.init_string(payload)
258 }
259
260 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 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 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 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 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 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 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 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 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}