1use std::cell::{Cell, RefCell};
2use std::mem::MaybeUninit;
3use std::rc::Rc;
4use std::{array, env};
5
6use crate::Error;
7use crate::rite::{Irep, Rite, insn};
8
9use super::op::Op;
10use super::prelude::prelude;
11use super::value::RHashMap;
12use super::value::*;
13use super::{op, optable::*};
14
15pub const VERSION: &str = env!("CARGO_PKG_VERSION");
16pub const ENGINE: &str = "mruby/edge";
17
18const MAX_REGS_SIZE: usize = 256;
19
20#[derive(Debug, Clone)]
21pub enum TargetContext {
22 Class(Rc<RClass>),
23 Module(Rc<RModule>),
24}
25
26impl TargetContext {
27 pub fn name(&self) -> String {
28 match self {
29 TargetContext::Class(c) => c.full_name(),
30 TargetContext::Module(m) => m.full_name(),
31 }
32 }
33}
34
35#[derive(Debug)]
36pub struct Breadcrumb {
37 pub event: &'static str, pub caller: Option<String>,
39 pub return_reg: Option<usize>,
40 pub upper: Option<Rc<Breadcrumb>>,
41}
42
43#[derive(Debug)]
44pub struct KArgs {
45 pub args: RefCell<RHashMap<RSym, Rc<RObject>>>,
46 pub kwrest_reg: Cell<usize>,
47 pub upper: Option<Rc<KArgs>>,
48}
49
50impl Breadcrumb {
51 #[cfg(feature = "mrubyedge-debug")]
52 pub fn display_breadcrumb_for_debug(&self, level: usize, max_level: usize) -> bool {
53 if level > max_level {
54 return false;
55 }
56 eprintln!(
57 "{}- Breadcrumb: event='{}', caller={}, return_reg={:?}",
58 " ".repeat(level),
59 self.event,
60 self.caller.as_deref().unwrap_or("(none)"),
61 self.return_reg
62 );
63 if let Some(upper) = &self.upper {
64 upper.display_breadcrumb_for_debug(level + 1, max_level);
65 }
66 true
67 }
68}
69
70pub struct VM {
71 pub irep: Rc<IREP>,
72
73 pub id: usize,
74 pub bytecode: Vec<u8>,
75 pub current_irep: Rc<IREP>,
76 pub pc: Cell<usize>,
77 pub regs: [Option<Rc<RObject>>; MAX_REGS_SIZE],
78 pub current_regs_offset: usize,
79 pub current_callinfo: Option<Rc<CALLINFO>>,
80 pub current_breadcrumb: Option<Rc<Breadcrumb>>,
81 pub kargs: RefCell<Option<RHashMap<RSym, Rc<RObject>>>>,
82 pub current_kargs: RefCell<Option<Rc<KArgs>>>,
83 pub target_class: TargetContext,
84 pub exception: Option<Rc<RException>>,
85
86 pub flag_preemption: Cell<bool>,
87
88 #[cfg(feature = "insn-limit")]
89 pub insn_count: Cell<usize>,
90 #[cfg(feature = "insn-limit")]
91 pub insn_limit: usize,
92
93 pub object_class: Rc<RClass>,
95 pub builtin_class_table: RHashMap<&'static str, Rc<RClass>>,
96 pub class_object_table: RHashMap<String, Rc<RObject>>,
97
98 pub globals: RHashMap<String, Rc<RObject>>,
99 pub consts: RHashMap<String, Rc<RObject>>,
100
101 pub upper: Option<Rc<ENV>>,
102 pub cur_env: RHashMap<usize, Rc<ENV>>,
104 pub has_env_ref: RHashMap<usize, bool>,
105
106 pub fn_table: RFnTable,
107 pub fn_block_stack: RFnStack,
108}
109
110pub struct RFnTable {
111 pub size: Cell<usize>,
112 pub table: [MaybeUninit<Rc<RFn>>; 4096],
113}
114
115impl RFnTable {
116 #[allow(clippy::new_without_default)]
117 pub fn new() -> Self {
118 Self {
119 size: Cell::new(0),
120 table: array::from_fn(|_| MaybeUninit::uninit()),
121 }
122 }
123
124 pub fn set(&mut self, f: Rc<RFn>) {
125 let i = self.size.get();
126 if i >= self.table.len() {
127 panic!("RFnTable overflow");
128 }
129
130 self.table[i].write(f);
131 let size = self.size.get();
132 if i >= size {
133 self.size.set(i + 1);
134 }
135 }
136
137 pub fn get(&self, i: usize) -> Option<Rc<RFn>> {
138 if i >= self.size.get() {
139 return None;
140 }
141
142 unsafe { self.table[i].assume_init_ref() }.clone().into()
143 }
144
145 pub fn len(&self) -> usize {
146 self.size.get()
147 }
148
149 pub fn is_empty(&self) -> bool {
150 self.size.get() == 0
151 }
152}
153
154pub struct RFnStack {
155 pub size: Cell<usize>,
156 pub stack: [Option<Rc<RFn>>; 64],
157}
158
159impl RFnStack {
160 #[allow(clippy::new_without_default)]
161 pub fn new() -> Self {
162 Self {
163 size: Cell::new(0),
164 stack: array::from_fn(|_| None),
165 }
166 }
167
168 pub fn push(&mut self, f: Rc<RFn>) -> Result<(), Error> {
169 let i = self.size.get();
170 if i >= self.stack.len() {
171 return Err(Error::internal("RFnStack overflow"));
172 }
173
174 self.stack[i] = Some(f);
175 let size = self.size.get();
176 if i >= size {
177 self.size.set(i + 1);
178 }
179 Ok(())
180 }
181
182 pub fn pop(&self) -> Result<Rc<RFn>, Error> {
183 let i = self.size.get();
184 if i == 0 {
185 return Err(Error::internal("RFnStack underflow"));
186 }
187
188 self.size.set(i - 1);
189
190 self.stack[i - 1]
191 .as_ref()
192 .cloned()
193 .ok_or_else(|| Error::internal("RFnStack invalid state"))
194 }
195
196 pub fn len(&self) -> usize {
197 self.size.get()
198 }
199
200 pub fn is_empty(&self) -> bool {
201 self.size.get() == 0
202 }
203}
204
205impl VM {
206 pub fn open(rite: &mut Rite) -> VM {
209 let irep = rite_to_irep(rite);
210
211 VM::new_by_raw_irep(irep)
212 }
213
214 pub fn empty() -> VM {
217 let irep = IREP {
218 __id: 0,
219 nlocals: 0,
220 nregs: 0,
221 rlen: 0,
222 code: vec![op::Op {
223 code: insn::OpCode::STOP,
224 operand: insn::Fetched::Z,
225 pos: 18,
226 len: 1,
227 }],
228 syms: Vec::new(),
229 pool: Vec::new(),
230 reps: Vec::new(),
231 lv: None,
232 catch_target_pos: Vec::new(),
233 };
234 Self::new_by_raw_irep(irep)
235 }
236
237 pub fn new_by_raw_irep(irep: IREP) -> VM {
241 let irep = Rc::new(irep);
242 let globals = RHashMap::default();
243 let consts = RHashMap::default();
244 let builtin_class_table = RHashMap::default();
245 let class_object_table = RHashMap::default();
246
247 let object_class = Rc::new(RClass::new("Object", None, None));
248 object_class.update_module_weakref();
249
250 let id = 1; let bytecode = Vec::new();
252 let current_irep = irep.clone();
253 let pc = Cell::new(0);
254 let regs: [Option<Rc<RObject>>; MAX_REGS_SIZE] = [const { None }; MAX_REGS_SIZE];
255 let current_regs_offset = 0;
256 let current_callinfo = None;
257 let current_breadcrumb = Some(Rc::new(Breadcrumb {
258 upper: None,
259 event: "root",
260 caller: None,
261 return_reg: None,
262 }));
263 let kargs = RefCell::new(None);
264 let current_kargs = RefCell::new(None);
265 let target_class = TargetContext::Class(object_class.clone());
266 let exception = None;
267 let flag_preemption = Cell::new(false);
268 let fn_table = RFnTable::new();
269 let fn_block_stack = RFnStack::new();
270 let upper = None;
271 let cur_env = RHashMap::default();
272 let has_env_ref = RHashMap::default();
273
274 #[cfg(feature = "insn-limit")]
275 let insn_count = Cell::new(0);
276 #[cfg(feature = "insn-limit")]
277 let insn_limit = {
278 let limit_str = env!(
279 "MRUBYEDGE_INSN_LIMIT",
280 "MRUBYEDGE_INSN_LIMIT must be set when insn-limit feature is enabled"
281 );
282 limit_str
283 .parse::<usize>()
284 .expect("MRUBYEDGE_INSN_LIMIT must be a valid number")
285 };
286
287 let mut vm = VM {
288 id,
289 bytecode,
290 irep,
291 current_irep,
292 pc,
293 regs,
294 current_regs_offset,
295 current_callinfo,
296 current_breadcrumb,
297 kargs,
298 current_kargs,
299 target_class,
300 exception,
301 flag_preemption,
302 #[cfg(feature = "insn-limit")]
303 insn_count,
304 #[cfg(feature = "insn-limit")]
305 insn_limit,
306 object_class,
307 builtin_class_table,
308 class_object_table,
309 globals,
310 consts,
311 upper,
312 cur_env,
313 has_env_ref,
314 fn_table,
315 fn_block_stack,
316 };
317
318 prelude(&mut vm);
319
320 vm
321 }
322
323 #[cfg(feature = "insn-limit")]
325 pub fn reset_insn_count(&mut self) {
326 self.insn_count.set(0);
327 }
328
329 #[cfg(feature = "insn-limit")]
331 pub fn get_insn_count(&self) -> usize {
332 self.insn_count.get()
333 }
334
335 pub fn run(&mut self) -> Result<Rc<RObject>, Box<dyn std::error::Error>> {
339 self.current_irep = self.irep.clone();
340 self.pc.set(0);
341
342 let upper = self.current_breadcrumb.take();
343 let new_breadcrumb = Rc::new(Breadcrumb {
344 upper,
345 event: "run",
346 caller: None,
347 return_reg: None,
348 });
349 self.current_breadcrumb.replace(new_breadcrumb);
350 self.__run()
351 }
352
353 pub fn run_internal(&mut self) -> Result<Rc<RObject>, Box<dyn std::error::Error>> {
355 let upper = self.current_breadcrumb.take();
356 let new_breadcrumb = Rc::new(Breadcrumb {
357 upper,
358 event: "run_internal",
359 caller: None,
360 return_reg: None,
361 });
362 self.current_breadcrumb.replace(new_breadcrumb);
363 self.__run()
364 }
365
366 pub fn eval_rite(
367 &mut self,
368 rite: &mut Rite,
369 ) -> Result<Rc<RObject>, Box<dyn std::error::Error>> {
370 let irep = rite_to_irep(rite);
371 self.pc.set(0);
372 self.current_irep = Rc::new(irep);
373
374 let upper = self.current_breadcrumb.take();
375 let new_breadcrumb = Rc::new(Breadcrumb {
376 upper,
377 event: "eval",
378 caller: None,
379 return_reg: None,
380 });
381 self.current_breadcrumb.replace(new_breadcrumb);
382 self.__run()
383 }
384
385 fn __run(&mut self) -> Result<Rc<RObject>, Box<dyn std::error::Error>> {
386 let class = self.object_class.clone();
387 let top_self = RObject {
389 tt: RType::Instance,
390 value: RValue::Instance(RInstance {
391 class,
392 ref_count: 1,
393 }),
394 object_id: 0.into(),
395 singleton_class: RefCell::new(None),
396 ivar: RefCell::new(RHashMap::default()),
397 }
398 .to_refcount_assigned();
399 if self.current_regs()[0].is_none() {
400 self.current_regs()[0].replace(top_self.clone());
401 }
402 let mut rescued = false;
403
404 loop {
405 if !rescued && let Some(e) = self.exception.clone() {
406 let operand = insn::Fetched::B(0);
407 let mut retreg = None;
408 if let Some(pos) = self.find_next_handler_pos() {
409 self.pc.set(pos);
410 rescued = true;
411 continue;
412 }
413
414 if let Error::BlockReturn(id, v) = e.error_type.borrow().clone()
415 && self.current_irep.__id == id
416 {
417 let operand = insn::Fetched::B(16); self.current_regs()[16].replace(v);
420 self.exception.take();
421 op_return(self, &operand).expect("[bug]cannot return");
422 continue;
423 }
424
425 if matches!(e.error_type.borrow().clone(), Error::Break(_)) {
426 retreg = match self.current_breadcrumb.as_ref() {
427 Some(bc) if bc.event == "do_op_send" => {
428 let retreg = bc.as_ref().return_reg.unwrap_or(0);
429 Some(retreg)
430 }
431 _ => None,
432 };
433 }
434 match op_return(self, &operand) {
435 Ok(_) => {}
436 Err(_) => {
437 if let Some(retreg) = retreg
438 && let Error::Break(brkval) = e.error_type.borrow().clone()
439 {
440 self.current_regs()[retreg].replace(brkval);
441 self.exception.take();
442 } else {
443 break;
444 }
445 }
446 }
447 if self.flag_preemption.get() {
448 break;
449 } else {
450 continue;
451 }
452 }
453 rescued = false;
454
455 let pc = self.pc.get();
456 if self.current_irep.code.len() <= pc {
457 break;
459 }
460 let op = *self
461 .current_irep
462 .code
463 .get(pc)
464 .ok_or_else(|| Error::internal("end of opcode reached"))?;
465 let operand = op.operand;
466 self.pc.set(pc + 1);
467
468 #[cfg(feature = "insn-limit")]
469 {
470 let count = self.insn_count.get();
471 if count >= self.insn_limit {
472 return Err(Error::internal(format!(
473 "instruction limit exceeded: {} instructions",
474 self.insn_limit
475 ))
476 .into());
477 }
478 self.insn_count.set(count + 1);
479 }
480
481 #[cfg(feature = "mrubyedge-debug")]
482 if let Ok(v) = env::var("MRUBYEDGE_DEBUG") {
483 let level: i32 = v.parse().unwrap_or(1);
484 if level >= 2 {
485 self.debug_dump_to_stdout(32);
486 }
487 eprintln!(
488 "{:?}: {:?} (pos={} len={})",
489 op.code, &operand, op.pos, op.len
490 );
491 }
492
493 match consume_expr(self, op.code, &operand, op.pos, op.len) {
494 Ok(_) => {}
495 Err(e) => {
496 let exception = RException::from_error(self, &e);
497 self.exception = Some(Rc::new(exception));
498 continue;
499 }
500 }
501
502 if self.flag_preemption.get() {
503 break;
504 }
505 }
506
507 self.flag_preemption.set(false);
508
509 if let Some(e) = self.exception.clone() {
510 return Err(e.error_type.borrow().clone().into());
511 }
512
513 let retval = match self.current_regs()[0].take() {
514 Some(v) => Ok(v),
515 None => Ok(Rc::new(RObject::nil())),
516 };
517 self.current_regs()[0].replace(top_self.clone());
518
519 retval
520 }
521
522 pub(crate) fn find_next_handler_pos(&mut self) -> Option<usize> {
523 let ci = self.pc.get();
524 for p in self.current_irep.catch_target_pos.iter() {
525 if ci < *p {
526 return Some(*p);
527 }
528 }
529 None
530 }
531
532 pub(crate) fn current_regs(&mut self) -> &mut [Option<Rc<RObject>>] {
533 &mut self.regs[self.current_regs_offset..]
534 }
535
536 pub(crate) fn get_current_regs_cloned(&mut self, i: usize) -> Result<Rc<RObject>, Error> {
537 self.current_regs()[i]
538 .clone()
539 .ok_or_else(|| Error::internal(format!("register {} is not assigned", i)))
540 }
541
542 pub(crate) fn take_current_regs(&mut self, i: usize) -> Result<Rc<RObject>, Error> {
543 self.current_regs()[i]
544 .take()
545 .ok_or_else(|| Error::internal(format!("register {} is not assigned", i)))
546 }
547
548 pub fn getself(&mut self) -> Result<Rc<RObject>, Error> {
551 self.get_current_regs_cloned(0)
552 }
553
554 pub fn must_getself(&mut self) -> Rc<RObject> {
557 self.current_regs()[0]
558 .clone()
559 .expect("self is not assigned")
560 }
561
562 pub fn get_kwargs(&self) -> Option<RHashMap<String, Rc<RObject>>> {
563 let kwargs = self.current_kargs.borrow().clone();
564 kwargs.map(|kargs| {
565 kargs
566 .args
567 .borrow()
568 .iter()
569 .map(|(k, v)| (k.name.clone(), v.clone()))
570 .collect()
571 })
572 }
573
574 pub(crate) fn register_fn(&mut self, f: RFn) -> usize {
575 self.fn_table.set(Rc::new(f));
576 self.fn_table.len() - 1
577 }
578
579 pub(crate) fn push_fnblock(&mut self, f: Rc<RFn>) -> Result<(), Error> {
580 self.fn_block_stack.push(f)
581 }
582
583 pub(crate) fn pop_fnblock(&mut self) -> Result<Rc<RFn>, Error> {
584 self.fn_block_stack.pop()
585 }
586
587 pub(crate) fn get_fn(&self, i: usize) -> Option<Rc<RFn>> {
588 self.fn_table.get(i)
589 }
590
591 pub fn get_class_by_name(&self, name: &str) -> Rc<RClass> {
594 self.builtin_class_table
595 .get(name)
596 .cloned()
597 .unwrap_or_else(|| panic!("Class {} not found", name))
598 }
599
600 pub fn get_module_by_name(&self, name: &str) -> Rc<RModule> {
601 match self.consts.get(name).cloned() {
602 Some(obj) => match &obj.value {
603 RValue::Module(m) => m.clone(),
604 _ => panic!("Module {} not found", name),
605 },
606 None => panic!("Module {} not found", name),
607 }
608 }
609
610 pub fn get_const_by_name(&self, name: &str) -> Option<Rc<RObject>> {
611 self.consts.get(name).cloned()
612 }
613
614 pub fn define_class(
618 &mut self,
619 name: &str,
620 superclass: Option<Rc<RClass>>,
621 parent_module: Option<Rc<RModule>>,
622 ) -> Rc<RClass> {
623 let superclass = match superclass {
624 Some(c) => c,
625 None => self.object_class.clone(),
626 };
627 let class = Rc::new(RClass::new(name, Some(superclass), parent_module.clone()));
628 class.update_module_weakref();
629
630 let object = RObject::class(class.clone(), self);
631 self.consts.insert(name.to_string(), object.clone());
632 if let Some(parent) = parent_module {
633 parent
634 .consts
635 .borrow_mut()
636 .insert(name.to_string(), object.clone());
637 } else {
638 self.object_class
639 .consts
640 .borrow_mut()
641 .insert(name.to_string(), object);
642 }
643 class
644 }
645
646 pub fn define_module(&mut self, name: &str, parent_module: Option<Rc<RModule>>) -> Rc<RModule> {
649 let module = Rc::new(RModule::new(name));
650 if let Some(parent) = parent_module {
651 module.parent.replace(Some(parent));
652 }
653 let object = RObject::module(module.clone()).to_refcount_assigned();
654 self.consts.insert(name.to_string(), object.clone());
655 self.object_class
656 .consts
657 .borrow_mut()
658 .insert(name.to_string(), object);
659 module
660 }
661
662 pub(crate) fn define_standard_class(&mut self, name: &'static str) -> Rc<RClass> {
663 let class = self.define_class(name, None, None);
664 self.builtin_class_table.insert(name, class.clone());
665 class
666 }
667
668 pub(crate) fn define_standard_class_with_superclass(
669 &mut self,
670 name: &'static str,
671 superclass: Rc<RClass>,
672 ) -> Rc<RClass> {
673 let class = self.define_class(name, Some(superclass.clone()), None);
674 self.builtin_class_table.insert(name, class.clone());
675 class
676 }
677
678 #[allow(dead_code)]
679 pub(crate) fn define_standard_class_under(
680 &mut self,
681 name: &'static str,
682 parent: Rc<RModule>,
683 ) -> Rc<RClass> {
684 let class = self.define_class(name, None, Some(parent));
685 self.builtin_class_table.insert(name, class.clone());
686 class
687 }
688
689 #[allow(dead_code)]
690 pub(crate) fn define_standard_class_with_superclass_under(
691 &mut self,
692 name: &'static str,
693 superclass: Rc<RClass>,
694 parent: Rc<RModule>,
695 ) -> Rc<RClass> {
696 let class = self.define_class(name, Some(superclass.clone()), Some(parent));
697 self.builtin_class_table.insert(name, class.clone());
698 class
699 }
700
701 #[allow(unused)]
702 pub fn debug_dump_to_stdout(&mut self, max_breadcrumb_level: usize) {
703 #[cfg(feature = "mrubyedge-debug")]
704 {
705 use crate::yamrb::helpers::mrb_call_inspect;
706 eprintln!("=== VM Dump ===");
707 eprintln!("ID: {}", self.id);
708 eprintln!("Current IREP ID: {}", self.current_irep.__id);
709 eprintln!("PC: {}", self.pc.get());
710 let current_regs_offset = self.current_regs_offset;
711 eprintln!("IREPs:");
712 self.current_irep
713 .code
714 .iter()
715 .enumerate()
716 .for_each(|(i, op)| {
717 eprintln!("{:04} {:?}: {:?}", i, op.code, op.operand);
718 });
719 eprintln!("Current Regs Offset: {}", current_regs_offset);
720 eprintln!("Regs:");
721 let size = self.regs.len();
722 for i in 0..size {
723 let reg = self.regs.get(i).unwrap().clone();
724 if let Some(obj) = reg {
725 let inspect: String = mrb_call_inspect(self, obj.clone())
726 .unwrap()
727 .as_ref()
728 .try_into()
729 .unwrap_or_else(|_| "(uninspectable)".into());
730 if i < current_regs_offset {
731 eprintln!(" R{}(--): {}(oid={})", i, inspect, obj.object_id.get());
732 } else {
733 eprintln!(
734 " R{}(R{}): {}(oid={})",
735 i,
736 i - current_regs_offset,
737 inspect,
738 obj.object_id.get()
739 );
740 }
741 } else if i < 16 || i < current_regs_offset {
742 eprintln!(" R{}(--): <None>", i);
743 } else {
744 break;
745 }
746 }
747 eprintln!("Target Class: {}", self.target_class.name());
749 eprintln!(
750 "Exception: {:?}",
751 self.exception
752 .as_deref()
753 .map(|e| e.error_type.borrow().clone())
754 );
755 eprintln!("--- Breadcrumb ---");
756 if let Some(bc) = &self.current_breadcrumb {
757 bc.display_breadcrumb_for_debug(0, max_breadcrumb_level);
758 } else {
759 eprintln!("(none)");
760 }
761 eprintln!("=== End of VM Dump ===");
762 }
763 }
764
765 pub fn get_outermost_env(&self) -> Option<Rc<ENV>> {
766 let mut env = self.upper.clone();
767 while let Some(e) = env.clone() {
768 if e.upper.is_none() {
769 return env;
770 }
771 env = e.upper.clone();
772 }
773 env
774 }
775}
776
777fn interpret_insn(mut insns: &[u8]) -> Vec<Op> {
778 let mut pos: usize = 0;
779 let mut ops = Vec::new();
780 while !insns.is_empty() {
781 let op = insns[0];
782 let opcode: insn::OpCode = op.try_into().unwrap();
783 let fetched = insn::FETCH_TABLE[op as usize](&mut insns).unwrap();
784 ops.push(Op::new(opcode, fetched, pos, 1 + fetched.len()));
785 pos += 1 + fetched.len();
786 }
787 ops
788}
789
790fn load_irep_1(reps: &mut [Irep], pos: usize) -> (IREP, usize) {
791 let irep = &mut reps[pos];
792 let mut irep1 = IREP {
793 __id: pos,
794 nlocals: irep.nlocals(),
795 nregs: irep.nregs(),
796 rlen: irep.rlen(),
797 code: Vec::new(),
798 syms: Vec::new(),
799 pool: Vec::new(),
800 reps: Vec::new(),
801 lv: None,
802 catch_target_pos: Vec::new(),
803 };
804 for sym in irep.syms.iter() {
805 irep1
806 .syms
807 .push(RSym::new(sym.to_string_lossy().to_string()));
808 }
809 for val in irep.pool.iter() {
810 match val {
811 crate::rite::PoolValue::Str(s) | crate::rite::PoolValue::SStr(s) => {
812 irep1.pool.push(RPool::Str(s.to_string_lossy().to_string()));
813 }
814 crate::rite::PoolValue::Int32(i) => {
815 irep1.pool.push(RPool::Int(*i as i64));
816 }
817 crate::rite::PoolValue::Int64(i) => {
818 irep1.pool.push(RPool::Int(*i));
819 }
820 crate::rite::PoolValue::Float(f) => {
821 irep1.pool.push(RPool::Float(*f));
822 }
823 crate::rite::PoolValue::BigInt(_) => {
824 irep1.pool.push(RPool::Int(0));
826 }
827 }
828 }
829 let code = interpret_insn(irep.insn);
830 for ch in irep.catch_handlers.iter() {
831 let pos = ch.target;
832 let (i, _) = code
833 .iter()
834 .enumerate()
835 .find(|(_, op)| op.pos == pos)
836 .expect("catch handler mismatch");
837 irep1.catch_target_pos.push(i);
838 }
839 let mut map = RHashMap::default();
840 for (reg, name) in irep.lv.iter().enumerate() {
841 if let Some(name) = name {
842 map.insert(reg + 1, name.to_string_lossy().to_string());
844 }
845 }
846 if !map.is_empty() {
847 irep1.lv = Some(map);
848 }
849 irep1.catch_target_pos.sort();
850
851 irep1.code = code;
852 (irep1, pos + 1)
853}
854
855fn load_irep_0(reps: &mut [Irep], pos: usize) -> (IREP, usize) {
856 let (mut irep0, newpos) = load_irep_1(reps, pos);
857 let mut pos = newpos;
858 for _ in 0..irep0.rlen {
859 let (rep, newpos) = load_irep_0(reps, pos);
860 pos = newpos;
861 irep0.reps.push(Rc::new(rep));
862 }
863 (irep0, pos)
864}
865
866fn rite_to_irep(rite: &mut Rite) -> IREP {
868 let (irep0, _) = load_irep_0(&mut rite.irep, 0);
869 irep0
870}
871
872#[derive(Debug, Clone)]
873pub struct IREP {
874 pub __id: usize,
875
876 pub nlocals: usize,
877 pub nregs: usize, pub rlen: usize,
879 pub code: Vec<Op>,
880 pub syms: Vec<RSym>,
881 pub pool: Vec<RPool>,
882 pub reps: Vec<Rc<IREP>>,
883 pub lv: Option<RHashMap<usize, String>>,
884 pub catch_target_pos: Vec<usize>,
885}
886
887#[derive(Debug, Clone)]
888pub struct CALLINFO {
889 pub prev: Option<Rc<CALLINFO>>,
890 pub method_id: RSym,
891 pub pc_irep: Rc<IREP>,
892 pub pc: usize,
893 pub current_regs_offset: usize,
894 pub target_class: TargetContext,
895 pub n_args: usize,
896 pub return_reg: usize,
897 pub method_owner: Option<Rc<RModule>>,
898}
899
900#[derive(Debug, Clone)]
901pub struct ENV {
902 pub __irep_id: usize,
903 pub upper: Option<Rc<ENV>>,
904 pub captured: RefCell<Option<Vec<Option<Rc<RObject>>>>>,
905 pub current_regs_offset: usize,
906 pub is_expired: Cell<bool>,
907}
908
909impl ENV {
910 #[allow(unused)]
911 pub(crate) fn has_captured(&self) -> bool {
912 self.captured.borrow().is_some()
913 }
914
915 #[allow(unused)]
916 pub(crate) fn capture(&self, regs: &[Option<Rc<RObject>>]) {
917 let mut captured = self.captured.borrow_mut();
918 captured.replace(regs.to_vec());
919 }
920
921 pub(crate) fn capture_no_clone(&self, regs: Vec<Option<Rc<RObject>>>) {
922 let mut captured = self.captured.borrow_mut();
923 captured.replace(regs);
924 }
925
926 pub(crate) fn expire(&self) {
927 self.is_expired.set(true);
928 }
929
930 pub(crate) fn expired(&self) -> bool {
931 self.is_expired.get()
932 }
933}