use std::cell::Cell;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::Error;
use crate::rite::insn::{Fetched, OpCode};
use super::prelude::object::mrb_object_is_equal;
use super::{helpers::mrb_funcall, value::*, vm::*};
const ENTER_M1_MASK: u32 = 0b11111 << 18;
const ENTER_O_MASK: u32 = 0b11111 << 13;
const ENTER_R_MASK: u32 = 0b1 << 12;
const ENTER_M2_MASK: u32 = 0b11111 << 7;
const ENTER_K_MASK: u32 = 0b11111 << 2;
const ENTER_D_MASK: u32 = 0b1 << 1;
const ENTER_B_MASK: u32 = 0b1 << 0;
pub(crate) fn consume_expr(
vm: &mut VM,
code: OpCode,
operand: &Fetched,
pos: usize,
len: usize,
) -> Result<(), Error> {
use crate::rite::insn::OpCode::*;
match code {
NOP => {
op_nop(vm, operand)?;
}
MOVE => {
op_move(vm, operand)?;
}
LOADL => {
op_loadl(vm, operand)?;
}
LOADI => {
op_loadi(vm, operand)?;
}
LOADINEG => {
op_loadineg(vm, operand)?;
}
LOADI__1 => {
op_loadi_n(vm, -1, operand)?;
}
LOADI_0 => {
op_loadi_n(vm, 0, operand)?;
}
LOADI_1 => {
op_loadi_n(vm, 1, operand)?;
}
LOADI_2 => {
op_loadi_n(vm, 2, operand)?;
}
LOADI_3 => {
op_loadi_n(vm, 3, operand)?;
}
LOADI_4 => {
op_loadi_n(vm, 4, operand)?;
}
LOADI_5 => {
op_loadi_n(vm, 5, operand)?;
}
LOADI_6 => {
op_loadi_n(vm, 6, operand)?;
}
LOADI_7 => {
op_loadi_n(vm, 7, operand)?;
}
LOADI16 => {
op_loadi16(vm, operand)?;
}
LOADI32 => {
op_loadi32(vm, operand)?;
}
LOADSYM => {
op_loadsym(vm, operand)?;
}
LOADNIL => {
op_loadnil(vm, operand)?;
}
LOADSELF => {
op_loadself(vm, operand)?;
}
LOADT => {
op_loadt(vm, operand)?;
}
LOADF => {
op_loadf(vm, operand)?;
}
GETGV => {
op_getgv(vm, operand)?;
}
SETGV => {
op_setgv(vm, operand)?;
}
GETIV => {
op_getiv(vm, operand)?;
}
SETIV => {
op_setiv(vm, operand)?;
}
GETCONST => {
op_getconst(vm, operand)?;
}
SETCONST => {
op_setconst(vm, operand)?;
}
GETMCNST => {
op_getmcnst(vm, operand)?;
}
GETUPVAR => {
op_getupvar(vm, operand)?;
}
SETUPVAR => {
op_setupvar(vm, operand)?;
}
GETIDX => {
op_getidx(vm, operand)?;
}
SETIDX => {
op_setidx(vm, operand)?;
}
JMP => {
op_jmp(vm, operand, pos + len)?;
}
JMPIF => {
op_jmpif(vm, operand, pos + len)?;
}
JMPNOT => {
op_jmpnot(vm, operand, pos + len)?;
}
JMPNIL => {
op_jmpnil(vm, operand, pos + len)?;
}
EXCEPT => {
op_except(vm, operand)?;
}
RESCUE => {
op_rescue(vm, operand)?;
}
RAISEIF => {
op_raiseif(vm, operand)?;
}
SSEND => {
op_ssend(vm, operand)?;
}
SSENDB => {
op_ssendb(vm, operand)?;
}
SEND => {
op_send(vm, operand)?;
}
SENDB => {
op_sendb(vm, operand)?;
}
CALL => {
op_call(vm, operand)?;
}
SUPER => {
op_super(vm, operand)?;
}
ENTER => {
op_enter(vm, operand)?;
}
RETURN => {
op_return(vm, operand)?;
}
ADD => {
op_add(vm, operand)?;
}
ADDI => {
op_addi(vm, operand)?;
}
SUB => {
op_sub(vm, operand)?;
}
SUBI => {
op_subi(vm, operand)?;
}
MUL => {
op_mul(vm, operand)?;
}
DIV => {
op_div(vm, operand)?;
}
EQ => {
op_eq(vm, operand)?;
}
LT => {
op_lt(vm, operand)?;
}
LE => {
op_le(vm, operand)?;
}
GT => {
op_gt(vm, operand)?;
}
GE => {
op_ge(vm, operand)?;
}
ARRAY => {
op_array(vm, operand)?;
}
ARRAY2 => {
op_array2(vm, operand)?;
}
SYMBOL => {
op_symbol(vm, operand)?;
}
STRING => {
op_string(vm, operand)?;
}
STRCAT => {
op_strcat(vm, operand)?;
}
HASH => {
op_hash(vm, operand)?;
}
LAMBDA => {
op_lambda(vm, operand)?;
}
BLOCK => {
op_block(vm, operand)?;
}
METHOD => {
op_method(vm, operand)?;
}
RANGE_INC => {
op_range_inc(vm, operand)?;
}
RANGE_EXC => {
op_range_exc(vm, operand)?;
}
OCLASS => {
op_oclass(vm, operand)?;
}
CLASS => {
op_class(vm, operand)?;
}
MODULE => {
op_module(vm, operand)?;
}
EXEC => {
op_exec(vm, operand)?;
}
DEF => {
op_def(vm, operand)?;
}
SCLASS => {
op_sclass(vm, operand)?;
}
TCLASS => {
op_tclass(vm, operand)?;
}
STOP => {
op_stop(vm, operand)?;
}
_ => {
unimplemented!("{:?}: Not supported yet", code)
}
}
Ok(())
}
pub(crate) fn push_callinfo(
vm: &mut VM,
method_id: RSym,
n_args: usize,
method_owner: Option<Rc<RModule>>,
) {
let callinfo = CALLINFO {
prev: vm.current_callinfo.clone(),
method_id,
pc_irep: vm.current_irep.clone(),
pc: vm.pc.get(),
current_regs_offset: vm.current_regs_offset,
n_args,
target_class: vm.target_class.clone(),
method_owner,
};
vm.current_callinfo = Some(Rc::new(callinfo));
}
fn calcurate_pc(irep: &IREP, pc: usize, original_pc: usize) -> usize {
let mut next_pc = pc;
loop {
let op = irep.code.get(next_pc).expect("cannot fetch op anymore");
if op.pos == original_pc {
break;
}
next_pc += 1;
}
next_pc
}
pub(crate) fn op_nop(_vm: &mut VM, _operand: &Fetched) -> Result<(), Error> {
Ok(())
}
pub(crate) fn op_loadi_n(vm: &mut VM, n: i32, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = RObject::integer(n as i64);
vm.current_regs()[a].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadl(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.current_irep.pool[b as usize].clone();
let val = Rc::new(RObject::string(val.as_str().to_string()));
vm.current_regs()[a as usize].replace(val);
Ok(())
}
pub(crate) fn op_loadi16(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bs()?;
let val = RObject::integer(b as i64);
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadi32(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bss()?;
let val = RObject::integer((b as i64) << 16 | c as i64);
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadi(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = RObject::integer(b as i64);
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadineg(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = RObject::integer(-(b as i64));
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadsym(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.current_irep.syms[b as usize].clone();
vm.current_regs()[a as usize].replace(Rc::new(RObject::symbol(val)));
Ok(())
}
pub(crate) fn op_loadnil(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = RObject::nil();
vm.current_regs()[a].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadself(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val: Rc<RObject> = vm.getself()?;
vm.current_regs()[a].replace(val);
Ok(())
}
pub(crate) fn op_loadt(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = RObject::boolean(true);
vm.current_regs()[a].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_loadf(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = RObject::boolean(false);
vm.current_regs()[a].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_getgv(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.current_irep.syms[b as usize].clone();
let val = vm
.globals
.get(&val.name)
.ok_or_else(|| Error::internal(format!("global variable not found {}", val.name)))?
.clone();
vm.current_regs()[a as usize].replace(val);
Ok(())
}
pub(crate) fn op_setgv(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.get_current_regs_cloned(a as usize)?;
let sym = vm.current_irep.syms[b as usize].clone();
vm.globals.insert(sym.name.clone(), val);
Ok(())
}
pub(crate) fn op_getiv(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let this = vm.getself()?;
let ivar = match &this.value {
RValue::Instance(ins) => ins
.ivar
.borrow()
.get(&vm.current_irep.syms[b as usize].name)
.ok_or_else(|| Error::internal(format!("symbol not found {}", b)))?
.clone(),
_ => unreachable!("getiv must be called on instance"),
};
vm.current_regs()[a as usize].replace(ivar);
Ok(())
}
pub(crate) fn op_setiv(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let this = vm.getself()?;
let val = vm.get_current_regs_cloned(a as usize)?;
match &this.value {
RValue::Instance(ins) => {
let mut ivar = ins.ivar.borrow_mut();
ivar.insert(vm.current_irep.syms[b as usize].name.clone(), val)
}
_ => unreachable!("setiv must be called on instance"),
};
Ok(())
}
pub(crate) fn op_getconst(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let name = vm.current_irep.syms[b as usize].name.clone();
let mut current = current_namespace(vm);
while let Some(ns) = current.clone() {
if let Some(val) = ns.consts.borrow().get(&name).cloned() {
vm.current_regs()[a as usize].replace(val);
return Ok(());
}
current = ns.parent.borrow().clone();
}
if let Some(val) = vm.consts.get(&name).cloned() {
vm.current_regs()[a as usize].replace(val);
return Ok(());
}
Err(Error::NameError(name))
}
pub(crate) fn op_setconst(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let name = vm.current_irep.syms[b as usize].name.clone();
let val = vm.get_current_regs_cloned(a as usize)?;
vm.consts.insert(name, val);
Ok(())
}
pub(crate) fn op_getmcnst(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let recv = vm.get_current_regs_cloned(a as usize)?;
let name = vm.current_irep.syms[b as usize].name.clone();
let mut module = match &recv.value {
RValue::Class(klass) => Some(klass.module.clone()),
RValue::Module(module) => Some(module.clone()),
_ => None,
};
while let Some(current) = module.clone() {
if let Some(val) = current.consts.borrow().get(&name).cloned() {
vm.current_regs()[a as usize].replace(val);
return Ok(());
}
module = current.parent.borrow().clone();
}
Err(Error::NameError(name.clone()))
}
pub(crate) fn op_getupvar(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
let n = c as usize;
let mut environ = vm
.upper
.as_ref()
.ok_or_else(|| Error::internal("op_getupvar expects upper env"))?;
for _ in 0..n {
environ = environ
.upper
.as_ref()
.ok_or_else(|| Error::internal("op_getupvar failed to find upvar"))?;
}
let environ = environ.clone();
let up_regs = &vm.regs[environ.current_regs_offset..];
if !environ.expired() {
if let Some(val) = up_regs[b as usize].as_ref().cloned() {
vm.current_regs()[a as usize].replace(val);
} else {
return Err(Error::internal(format!("register {} is empty", b)));
}
} else {
let captured = environ.captured.borrow();
let val = &captured
.as_ref()
.ok_or_else(|| Error::internal("captured environment not found"))?[b as usize];
let val = val.clone();
vm.current_regs()[a as usize]
.replace(val.ok_or_else(|| Error::internal("captured value not found"))?);
}
Ok(())
}
pub(crate) fn op_setupvar(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
let n = c as usize;
let mut environ = vm
.upper
.as_ref()
.ok_or_else(|| Error::internal("op_getupvar expects upper env"))?;
for _ in 0..n {
environ = environ
.upper
.as_ref()
.ok_or_else(|| Error::internal("op_getupvar failed to find upvar"))?;
}
let environ = environ.clone();
let current_regs_offset = environ.current_regs_offset;
let val = vm.get_current_regs_cloned(a as usize)?;
if !environ.expired() {
let up_regs = &mut vm.regs[current_regs_offset..];
let target = &mut up_regs[b as usize];
target.replace(val);
} else {
let mut captured = environ.captured.borrow_mut();
let captured = captured
.as_mut()
.ok_or_else(|| Error::internal("captured environment not found"))?;
let target = &mut captured[b as usize];
target.replace(val);
}
Ok(())
}
pub(crate) fn op_getidx(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let recv = vm.get_current_regs_cloned(a)?;
let idx = vm.get_current_regs_cloned(a + 1)?;
let args = vec![idx];
let val = mrb_funcall(vm, Some(recv), "[]", &args)?;
vm.current_regs()[a].replace(val);
Ok(())
}
pub(crate) fn op_setidx(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let recv = vm.get_current_regs_cloned(a)?;
let idx = vm.get_current_regs_cloned(a + 1)?;
let val = vm.get_current_regs_cloned(a + 2)?;
let args = vec![idx, val];
mrb_funcall(vm, Some(recv), "[]=", &args)?;
Ok(())
}
pub(crate) fn op_jmp(vm: &mut VM, operand: &Fetched, end_pos: usize) -> Result<(), Error> {
let a = operand.as_s()?;
let next_pc = calcurate_pc(&vm.current_irep, vm.pc.get(), end_pos + a as usize);
vm.pc.set(next_pc);
Ok(())
}
pub(crate) fn op_jmpif(vm: &mut VM, operand: &Fetched, end_pos: usize) -> Result<(), Error> {
let (a, b) = operand.as_bs()?;
let val = vm.get_current_regs_cloned(a as usize)?;
if val.is_truthy() {
let next_pc = calcurate_pc(&vm.current_irep, vm.pc.get(), end_pos + b as usize);
vm.pc.set(next_pc);
}
Ok(())
}
pub(crate) fn op_jmpnot(vm: &mut VM, operand: &Fetched, end_pos: usize) -> Result<(), Error> {
let (a, b) = operand.as_bs()?;
let val = vm.get_current_regs_cloned(a as usize)?;
if val.is_falsy() {
let next_pc = calcurate_pc(&vm.current_irep, vm.pc.get(), end_pos + b as usize);
vm.pc.set(next_pc);
}
Ok(())
}
pub(crate) fn op_jmpnil(vm: &mut VM, operand: &Fetched, end_pos: usize) -> Result<(), Error> {
let (a, b) = operand.as_bs()?;
let val = vm.get_current_regs_cloned(a as usize)?;
if val.is_nil() {
let next_pc = calcurate_pc(&vm.current_irep, vm.pc.get(), end_pos + b as usize);
vm.pc.set(next_pc);
}
Ok(())
}
pub(crate) fn op_except(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()?;
let val = vm
.exception
.take()
.ok_or_else(|| Error::internal("exception not found"))?;
let exc = Rc::new(RObject::exception(val));
vm.current_regs()[a as usize].replace(exc);
Ok(())
}
pub(crate) fn op_rescue(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.get_current_regs_cloned(a as usize)?;
let exc_klass = vm.take_current_regs(b as usize)?;
match (&val.value, exc_klass.value.clone()) {
(RValue::Exception(exc), RValue::Class(klass)) => {
let etype = exc.error_type.borrow();
let is_rescued = etype.is_a(vm, klass);
let val = RObject::boolean(is_rescued);
vm.current_regs()[b as usize].replace(val.to_refcount_assigned());
}
_ => unreachable!("rescue must be called on exception"),
};
Ok(())
}
pub(crate) fn op_raiseif(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()?;
let val = vm.current_regs()[a as usize].as_ref().cloned();
if let Some(val) = val
&& let RValue::Exception(e) = &val.value
{
return Err(e.as_ref().error_type.borrow().clone());
}
Ok(())
}
pub(crate) fn op_move(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val = vm.get_current_regs_cloned(b as usize)?;
vm.current_regs()[a as usize].replace(val);
Ok(())
}
pub(crate) fn op_ssend(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
do_op_send(vm, 0, None, a, b, c)
}
pub(crate) fn op_ssendb(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
do_op_send(vm, 0, Some(a as usize + c as usize + 1), a, b, c)
}
pub(crate) fn op_send(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
do_op_send(vm, a as usize, None, a, b, c)
}
pub(crate) fn op_sendb(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
do_op_send(vm, a as usize, Some(a as usize + c as usize + 1), a, b, c)
}
pub(crate) fn do_op_send(
vm: &mut VM,
recv_index: usize,
blk_index: Option<usize>,
a: u8,
b: u8,
c: u8,
) -> Result<(), Error> {
let block_index = (a + c + 1) as usize;
let recv = vm.get_current_regs_cloned(recv_index)?;
let mut args = (0..c)
.map(|i| {
vm.get_current_regs_cloned((a + i + 1) as usize)
.expect("args too short for required")
})
.collect::<Vec<_>>();
if let Some(blk_index) = blk_index {
args.push(vm.get_current_regs_cloned(blk_index)?);
} else {
vm.current_regs()[block_index].replace(Rc::new(RObject::nil()));
args.push(Rc::new(RObject::nil()));
}
let method_id = vm.current_irep.syms[b as usize].clone();
let klass = recv.get_singleton_class_or_class(vm);
let (owner_module, method) = resolve_method(&klass, &method_id.name)
.ok_or_else(|| Error::NoMethodError(method_id.name.clone()))?;
vm.current_regs()[a as usize].replace(recv.clone());
if !method.is_rb_func {
let func = vm
.get_fn(method.func.unwrap())
.ok_or_else(|| Error::internal("function not found"))?;
vm.current_regs_offset += a as usize;
let res = func(vm, &args);
vm.current_regs_offset -= a as usize;
for i in (a as usize + 1)..block_index {
vm.current_regs()[i].take();
}
match res {
Ok(val) => {
vm.current_regs()[a as usize].replace(val);
}
Err(e) => {
vm.current_regs()[a as usize].replace(Rc::new(RObject::nil()));
return Err(e);
}
}
return Ok(());
}
push_callinfo(vm, method_id, c as usize, Some(owner_module));
vm.pc.set(0);
vm.current_irep = method.irep.ok_or_else(|| Error::internal("empry irep"))?;
vm.current_regs_offset += a as usize;
Ok(())
}
pub(crate) fn op_call(vm: &mut VM, _operand: &Fetched) -> Result<(), Error> {
push_callinfo(vm, "<tailcall>".into(), 0, None);
vm.pc.set(0);
let proc = vm.current_regs()[0]
.as_ref()
.cloned()
.ok_or_else(|| Error::internal("proc not found"))?;
match &proc.value {
RValue::Proc(proc) => {
vm.current_irep = proc
.irep
.as_ref()
.ok_or_else(|| Error::internal("empry irep"))?
.clone();
}
_ => unreachable!("call must be called on proc"),
}
Ok(())
}
pub(crate) fn op_super(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let callinfo = vm
.current_callinfo
.as_ref()
.ok_or_else(|| Error::internal("no current callinfo"))?;
let sym_id = callinfo.method_id.name.clone();
let owner_module = callinfo
.method_owner
.clone()
.ok_or_else(|| Error::RuntimeError("super called outside of method".to_string()))?;
let recv = vm.getself()?;
let args = (0..b)
.map(|i| {
vm.get_current_regs_cloned((a + i + 1) as usize)
.expect("args too short for super")
})
.collect::<Vec<_>>();
let klass = match &recv.value {
RValue::Instance(ins) => ins.class.clone(),
_ => unreachable!("super must be called on instance"),
};
let (next_owner, method) = resolve_next_method(&klass, &sym_id, &owner_module)
.ok_or_else(|| Error::NoMethodError(sym_id.clone()))?;
if !method.is_rb_func {
let func = vm.get_fn(method.func.unwrap()).ok_or_else(|| {
Error::internal(format!("functon registerd but no entry found: {}", &sym_id))
})?;
let res = func(vm, &args);
for i in (a as usize + 1)..(a as usize + b as usize + 1) {
vm.current_regs()[i].take();
}
match res {
Ok(val) => {
vm.current_regs()[a as usize].replace(val);
}
Err(e) => {
vm.current_regs()[a as usize].replace(Rc::new(RObject::nil()));
return Err(e);
}
}
return Ok(());
}
vm.current_regs()[a as usize].replace(recv.clone());
push_callinfo(
vm,
method.sym_id.clone().unwrap(),
b as usize,
Some(next_owner),
);
vm.pc.set(0);
vm.current_irep = method
.irep
.as_ref()
.ok_or_else(|| Error::internal("empty irep"))?
.clone();
vm.current_regs_offset += a as usize;
Ok(())
}
#[allow(dead_code)]
struct EnterArgInfo {
m1: u32,
o: u32,
r: u32,
m2: u32,
k: u32,
d: u32,
b: u32,
}
impl From<u32> for EnterArgInfo {
fn from(val: u32) -> Self {
EnterArgInfo {
m1: (val & ENTER_M1_MASK) >> 18,
o: (val & ENTER_O_MASK) >> 13,
r: (val & ENTER_R_MASK) >> 12,
m2: (val & ENTER_M2_MASK) >> 7,
k: (val & ENTER_K_MASK) >> 2,
d: (val & ENTER_D_MASK) >> 1,
b: (val & ENTER_B_MASK),
}
}
}
pub(crate) fn op_enter(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_w()?;
let arg_info = EnterArgInfo::from(a);
let m1_argc = arg_info.m1 as usize;
for i in 0..m1_argc {
match vm.current_regs()[i + 1].as_ref() {
Some(_) => {}
None => {
unreachable!("argument {} not passed", i + 1);
}
}
}
Ok(())
}
pub(crate) fn op_return(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let old_irep = vm.current_irep.clone();
let nregs = old_irep.nregs;
let regs0_cloned: Vec<_> = vm.current_regs()[0..nregs].to_vec();
if let Some(environ) = vm.cur_env.get(&vm.current_irep.__id) {
environ.capture_no_clone(regs0_cloned);
environ.as_ref().expire();
vm.has_env_ref.remove(&vm.current_irep.__id);
}
let regs0 = vm.current_regs();
if let Some(regs_a) = regs0[a].take() {
regs0[0].replace(regs_a);
}
if nregs > 0 {
regs0[1..=nregs].iter_mut().for_each(|reg| {
reg.take();
});
}
let ci = vm.current_callinfo.take();
if ci.is_none() {
if let Some(e) = &vm.exception {
return Err(e.error_type.borrow().clone());
}
vm.flag_preemption.set(true);
return Ok(());
}
let ci = ci.unwrap();
if let Some(prev) = &ci.prev {
vm.current_callinfo.replace(prev.clone());
}
vm.current_irep = ci.pc_irep.clone();
vm.pc.set(ci.pc);
vm.current_regs_offset = ci.current_regs_offset;
vm.target_class = ci.target_class.clone();
if vm.current_regs()[0].is_none() {
todo!("debug");
}
Ok(())
}
pub(crate) fn op_add(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => Rc::new(RObject::integer(n1 + n2)),
(RValue::Float(n1), RValue::Float(n2)) => Rc::new(RObject::float(n1 + n2)),
(RValue::Integer(n1), RValue::Float(n2)) => Rc::new(RObject::float(*n1 as f64 + n2)),
(RValue::Float(n1), RValue::Integer(n2)) => Rc::new(RObject::float(n1 + *n2 as f64)),
(RValue::String(n1), RValue::String(n2)) => {
let mut n1 = n1.borrow_mut();
let n2 = n2.borrow();
for c in n2.iter() {
n1.push(*c);
}
val1.clone()
}
_ => {
let args = vec![val2.clone()];
mrb_funcall(vm, Some(val1.clone()), "+", &args)?
}
};
vm.current_regs()[a].replace(result);
Ok(())
}
pub(crate) fn op_addi(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val1 = vm.take_current_regs(a as usize)?;
let val2 = b as i64;
let result = match &val1.value {
RValue::Integer(n1) => RObject::integer(*n1 + val2),
_ => {
unreachable!("addi supports only integer")
}
};
vm.current_regs()[a as usize].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_sub(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::integer(n1 - n2),
_ => {
unreachable!("sub supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_subi(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let val1 = vm.take_current_regs(a as usize)?;
let val2 = b as i64;
let result = match &val1.value {
RValue::Integer(n1) => RObject::integer(*n1 - val2),
_ => {
unreachable!("subi supports only integer")
}
};
vm.current_regs()[a as usize].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_mul(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::integer(n1 * n2),
_ => {
unreachable!("mul supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_div(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::integer(n1 / n2),
_ => {
unreachable!("div supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_lt(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::boolean(n1 < n2),
_ => {
unreachable!("lt supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_le(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::boolean(n1 <= n2),
_ => {
unreachable!("le supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_eq(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let lhs = vm.take_current_regs(a)?;
let rhs = vm.get_current_regs_cloned(b)?;
let result = mrb_object_is_equal(vm, lhs, rhs);
vm.current_regs()[a].replace(result);
Ok(())
}
pub(crate) fn op_gt(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::boolean(n1 > n2),
_ => {
unreachable!("gt supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_ge(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.take_current_regs(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let result = match (&val1.value, &val2.value) {
(RValue::Integer(n1), RValue::Integer(n2)) => RObject::boolean(n1 >= n2),
_ => {
unreachable!("ge supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
Ok(())
}
pub(crate) fn op_array(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
do_op_array(vm, a as usize, a as usize, b as usize)
}
pub(crate) fn op_array2(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b, c) = operand.as_bbb()?;
do_op_array(vm, a as usize, b as usize, c as usize)
}
fn do_op_array(vm: &mut VM, this: usize, start: usize, n: usize) -> Result<(), Error> {
let mut ary = Vec::with_capacity(n);
for i in 0..n {
if this == start && i == 0 {
ary.push(vm.take_current_regs(start)?);
} else {
ary.push(vm.get_current_regs_cloned(start + i)?);
}
}
let val = RObject::array(ary);
vm.current_regs()[this].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_symbol(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let symstr = vm.current_irep.pool[b as usize].as_str().to_string();
let sym = RSym::new(symstr);
let val = RObject::symbol(sym);
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_string(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let str = vm.current_irep.pool[b as usize].as_str().to_string();
let val = RObject::string(str);
vm.current_regs()[a as usize].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_strcat(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let b = a + 1;
let val1 = vm.get_current_regs_cloned(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
match (&val1.value, &val2.value) {
(RValue::String(s1), RValue::String(s2)) => {
let mut s1 = s1.borrow_mut();
let s2 = s2.borrow();
for c in s2.iter() {
s1.push(*c);
}
}
(RValue::String(s1), RValue::Integer(s2)) => {
let mut s1 = s1.borrow_mut();
let s2 = s2.to_string();
for c in s2.as_bytes() {
s1.push(*c);
}
}
_ => {
unreachable!("strcat supports only string")
}
};
Ok(())
}
pub(crate) fn op_hash(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let a = a as usize;
let b = b as usize;
let mut hash = HashMap::new();
for i in 0..b {
let key = vm.get_current_regs_cloned(a + i * 2)?;
let val = vm.get_current_regs_cloned(a + i * 2 + 1)?;
hash.insert(key.as_hash_key()?, (key, val));
}
let val = RObject::hash(hash);
vm.current_regs()[a].replace(Rc::new(val));
Ok(())
}
pub(crate) fn op_lambda(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let irep = Some(vm.current_irep.reps[b as usize].clone());
let environ = ENV {
upper: vm.upper.clone(),
current_regs_offset: vm.current_regs_offset,
is_expired: Cell::new(false),
captured: RefCell::new(None),
};
let environ = Rc::new(environ);
vm.cur_env.insert(vm.current_irep.__id, environ.clone());
vm.has_env_ref.insert(vm.current_irep.__id, true);
let val = RObject {
tt: RType::Proc,
value: RValue::Proc(RProc {
irep,
is_rb_func: true,
sym_id: Some("<lambda>".into()),
next: None,
func: None,
environ: Some(environ),
block_self: Some(vm.getself()?),
}),
object_id: u64::MAX.into(),
singleton_class: RefCell::new(None),
};
vm.current_regs()[a as usize].replace(val.to_refcount_assigned());
Ok(())
}
pub(crate) fn op_block(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let irep = Some(vm.current_irep.reps[b as usize].clone());
let environ = ENV {
upper: vm.upper.clone(),
current_regs_offset: vm.current_regs_offset,
is_expired: Cell::new(false),
captured: RefCell::new(None),
};
let environ = Rc::new(environ);
vm.cur_env.insert(vm.current_irep.__id, environ.clone());
vm.has_env_ref.insert(vm.current_irep.__id, true);
let val = RObject {
tt: RType::Proc,
value: RValue::Proc(RProc {
irep,
is_rb_func: true,
sym_id: Some("<block>".into()),
next: None,
func: None,
environ: Some(environ),
block_self: Some(vm.getself()?),
}),
object_id: u64::MAX.into(),
singleton_class: RefCell::new(None),
};
vm.current_regs()[a as usize].replace(val.to_refcount_assigned());
Ok(())
}
pub(crate) fn op_method(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let irep = Some(vm.current_irep.reps[b as usize].clone());
let val = RObject {
tt: super::value::RType::Proc,
value: super::value::RValue::Proc(super::value::RProc {
irep,
is_rb_func: true,
sym_id: None,
next: None,
func: None,
environ: None,
block_self: None,
}),
object_id: u64::MAX.into(),
singleton_class: RefCell::new(None),
};
vm.current_regs()[a as usize].replace(val.to_refcount_assigned());
Ok(())
}
pub(crate) fn op_range_inc(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()?;
do_op_range(vm, a as usize, a as usize + 1, false)
}
pub(crate) fn op_range_exc(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()?;
do_op_range(vm, a as usize, a as usize + 1, true)
}
fn do_op_range(vm: &mut VM, a: usize, b: usize, exclusive: bool) -> Result<(), Error> {
let val1 = vm.get_current_regs_cloned(a)?;
let val2 = vm.get_current_regs_cloned(b)?;
let val = RObject {
tt: super::value::RType::Range,
value: super::value::RValue::Range(val1, val2, exclusive),
object_id: u64::MAX.into(),
singleton_class: RefCell::new(None),
};
vm.current_regs()[a].replace(val.to_refcount_assigned());
Ok(())
}
pub(crate) fn op_oclass(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = RObject::class(vm.object_class.clone(), vm);
vm.current_regs()[a].replace(val);
Ok(())
}
fn current_namespace(vm: &mut VM) -> Option<Rc<RModule>> {
match vm.current_regs()[0].as_ref() {
Some(obj) => match &obj.value {
RValue::Class(klass) => Some(klass.module.clone()),
RValue::Module(module) => Some(module.clone()),
_ => None,
},
None => None,
}
}
pub(crate) fn op_class(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let superclass = vm.current_regs()[a as usize + 1].as_ref().cloned();
let name = vm.current_irep.syms[b as usize].clone();
let superclass = match superclass {
Some(superclass) => {
if let RValue::Class(klass) = &superclass.value {
klass.clone()
} else {
vm.object_class.clone()
}
}
None => vm.object_class.clone(),
};
let parent_module = current_namespace(vm);
let name = name.name;
let klass = vm.define_class(&name, Some(superclass), parent_module.clone());
let class_value = RObject::class(klass.clone(), vm);
if let Some(parent) = parent_module {
parent.consts.borrow_mut().insert(name.clone(), class_value);
} else {
vm.consts.insert(name.clone(), class_value);
}
let class_value = RObject::class(klass.clone(), vm);
vm.current_regs()[a as usize].replace(class_value);
Ok(())
}
pub(crate) fn op_module(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let name = vm.current_irep.syms[b as usize].clone();
let name = name.name;
let parent_module = current_namespace(vm);
let module = vm.define_module(&name, parent_module.clone());
let module_value = RObject::module(module.clone()).to_refcount_assigned();
if let Some(parent) = parent_module {
parent
.consts
.borrow_mut()
.insert(name.clone(), module_value);
} else {
vm.consts.insert(name.clone(), module_value);
}
vm.current_regs()[a as usize].replace(Rc::new(module.into()));
Ok(())
}
pub(crate) fn op_exec(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let recv = vm.get_current_regs_cloned(a as usize)?;
vm.current_regs()[a as usize].replace(recv.clone());
push_callinfo(vm, "<exec>".into(), 0, None);
vm.pc.set(0);
let irep = vm.current_irep.reps[b as usize].clone();
vm.current_irep = irep;
vm.current_regs_offset += a as usize;
vm.target_class = match &recv.value {
RValue::Class(klass) => TargetContext::Class(klass.clone()),
RValue::Module(module) => TargetContext::Module(module.clone()),
_ => TargetContext::Class(recv.get_class(vm)),
};
Ok(())
}
pub(crate) fn op_def(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let target = vm.get_current_regs_cloned(a as usize)?;
let method = vm.get_current_regs_cloned(a as usize + 1)?;
let sym = vm.current_irep.syms[b as usize].clone();
let target_ref = target.as_ref();
let method_ref = method.as_ref();
match (&target_ref.value, &method_ref.value) {
(RValue::Class(klass), RValue::Proc(method)) => {
let mut procs = klass.procs.borrow_mut();
let mut method = method.clone();
method.sym_id = Some(sym.clone());
procs.insert(sym.name.clone(), method);
}
(RValue::Module(module), RValue::Proc(method)) => {
let mut procs = module.procs.borrow_mut();
let mut method = method.clone();
method.sym_id = Some(sym.clone());
procs.insert(sym.name.clone(), method);
}
(_, RValue::Proc(method)) => {
let robject = target.clone();
let sclass = robject.get_singleton_class_or_class(vm);
let mut procs = sclass.procs.borrow_mut();
let mut method = method.clone();
method.sym_id = Some(sym.clone());
procs.insert(sym.name.clone(), method);
}
_ => {
unreachable!("DEF must be called with Proc");
}
}
vm.current_regs()[a as usize].replace(RObject::symbol(sym).to_refcount_assigned());
Ok(())
}
pub(crate) fn op_sclass(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val = vm.getself()?;
let singleton_class = val.singleton_class.borrow().clone();
if let Some(ref sc) = singleton_class {
let robj = RObject::class(sc.clone(), vm);
vm.current_regs()[a].replace(robj);
return Ok(());
}
Ok(())
}
pub(crate) fn op_tclass(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_b()? as usize;
let val: Rc<RObject> = match &vm.target_class {
TargetContext::Class(klass) => RObject::class(klass.clone(), vm),
TargetContext::Module(module) => Rc::new(module.clone().into()),
};
vm.current_regs()[a].replace(val);
Ok(())
}
pub(crate) fn op_stop(vm: &mut VM, _operand: &Fetched) -> Result<(), Error> {
vm.flag_preemption.set(true);
Ok(())
}