use crate::ExprType;
use crate::Scope;
use crate::bytecode::{
self, ErrorHandlerMode, ExitCode, Opcode, Register, TaggedRegisterRef, opcode_of,
};
use crate::image::Image;
use crate::mem::{ArrayData, ConstantDatum, DatumPtr, Heap, HeapDatum};
use crate::num::unchecked_usize_as_u8;
use crate::reader::LineCol;
const DEFAULT_MAX_CALL_STACK: usize = 4096;
const CALL_STACK_OVERFLOW_MSG: &str = "Out of call stack space";
type Address = usize;
pub(super) enum InternalStopReason {
End(ExitCode),
Eof,
Exception(Address, String),
Upcall(u16, Register, Address),
UpcallAsync(u16, Register, Address),
Yield,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum ErrorHandler {
None,
Jump { active: bool, addr: Address },
ResumeNext,
}
struct Frame {
old_pc: Address,
old_fp: usize,
ret_reg: Option<Register>,
}
#[inline(always)]
fn checked_add_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
lhs.checked_add(rhs).ok_or("Integer overflow")
}
#[inline(always)]
fn checked_and_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
Ok(lhs & rhs)
}
#[inline(always)]
fn checked_div_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
if rhs == 0 { Err("Division by zero") } else { lhs.checked_div(rhs).ok_or("Integer underflow") }
}
#[inline(always)]
fn checked_mod_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
if rhs == 0 { Err("Modulo by zero") } else { lhs.checked_rem(rhs).ok_or("Integer underflow") }
}
#[inline(always)]
fn checked_mul_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
lhs.checked_mul(rhs).ok_or("Integer overflow")
}
#[inline(always)]
fn checked_or_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
Ok(lhs | rhs)
}
#[inline(always)]
fn checked_pow_integer(lhs: i32, exp: u32) -> Result<i32, &'static str> {
lhs.checked_pow(exp).ok_or("Integer overflow")
}
#[inline(always)]
fn checked_shl_integer(lhs: i32, rhs: i32) -> Result<i32, String> {
match u32::try_from(rhs) {
Err(_) => Err(format!("Number of bits to << ({}) must be positive", rhs)),
Ok(bits) => Ok(lhs.checked_shl(bits).unwrap_or(0)),
}
}
#[inline(always)]
fn checked_shr_integer(lhs: i32, rhs: i32) -> Result<i32, String> {
match u32::try_from(rhs) {
Err(_) => Err(format!("Number of bits to >> ({}) must be positive", rhs)),
Ok(bits) => Ok(match lhs.checked_shr(bits) {
Some(i) => i,
None if lhs < 0 => -1,
None => 0,
}),
}
}
#[inline(always)]
fn checked_sub_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
lhs.checked_sub(rhs).ok_or("Integer underflow")
}
#[inline(always)]
fn checked_xor_integer(lhs: i32, rhs: i32) -> Result<i32, &'static str> {
Ok(lhs ^ rhs)
}
pub(super) struct Context {
pc: Address,
fp: usize,
stop: Option<InternalStopReason>,
err_handler: ErrorHandler,
regs: Vec<u64>,
call_stack: Vec<Frame>,
max_call_stack: usize,
yield_pending: bool,
}
impl Default for Context {
fn default() -> Self {
Self::new(DEFAULT_MAX_CALL_STACK)
}
}
impl Context {
pub(super) fn new(max_call_stack: usize) -> Self {
Self {
pc: 0,
fp: usize::from(Register::MAX_GLOBAL),
stop: None,
err_handler: ErrorHandler::None,
regs: vec![0; usize::from(Register::MAX)],
call_stack: vec![],
max_call_stack,
yield_pending: false,
}
}
}
impl Context {
fn reg_index(&self, reg: Register) -> usize {
let (is_global, index) = reg.to_parts();
let mut index = usize::from(index);
if !is_global {
index += self.fp;
}
index
}
fn get_reg(&self, reg: Register) -> u64 {
let index = self.reg_index(reg);
self.regs.get(index).copied().unwrap_or(0)
}
fn set_reg(&mut self, reg: Register, value: u64) {
let index = self.reg_index(reg);
if index >= self.regs.len() {
self.regs.resize(index + 1, 0);
}
self.regs[index] = value;
}
pub(super) fn set_pc(&mut self, pc: Address) {
self.pc = pc;
}
pub(super) fn error_handler(&self) -> ErrorHandler {
self.err_handler
}
pub(super) fn clear_error_handler(&mut self) {
self.err_handler = ErrorHandler::None;
}
pub(super) fn set_error_handler_active(&mut self) {
self.err_handler = match self.err_handler {
ErrorHandler::Jump { active: false, addr } => ErrorHandler::Jump { active: true, addr },
_ => unreachable!("Only inactive jump handlers can be activated"),
};
}
fn deref_string<'b>(
&self,
reg: Register,
constants: &'b [ConstantDatum],
heap: &'b Heap,
) -> &'b str {
let raw_addr = self.get_reg(reg);
DatumPtr::from(raw_addr).resolve_string(constants, heap)
}
pub(super) fn get_global_reg_raw(&self, index: u8) -> u64 {
self.regs[usize::from(index)]
}
pub(super) fn get_program_reg_raw(&self, index: u8) -> u64 {
self.regs[usize::from(Register::MAX_GLOBAL) + usize::from(index)]
}
fn resolve_array_index(
&mut self,
arr_reg: Register,
first_sub_reg: Register,
heap: &Heap,
) -> Option<(usize, usize)> {
let arr_ptr = DatumPtr::from(self.get_reg(arr_reg));
let heap_idx = arr_ptr.heap_index();
let array = match heap.get(heap_idx) {
HeapDatum::Array(a) => a,
_ => unreachable!("Register must point to an array"),
};
let ndims = array.dimensions.len();
let (_, first_idx) = first_sub_reg.to_parts();
let mut subscripts = Vec::with_capacity(ndims);
for i in 0..unchecked_usize_as_u8(ndims) {
let sub_reg = Register::local(first_idx + i).unwrap();
subscripts.push(self.get_reg(sub_reg) as i32);
}
match array.flat_index(&subscripts) {
Ok(flat_idx) => Some((heap_idx, flat_idx)),
Err(e) => {
self.set_exception(e);
None
}
}
}
fn set_exception<S: Into<String>>(&mut self, message: S) {
self.stop = Some(InternalStopReason::Exception(self.pc, message.into()));
}
fn push_frame(&mut self, frame: Frame) -> bool {
if self.call_stack.len() >= self.max_call_stack {
self.set_exception(CALL_STACK_OVERFLOW_MSG);
false
} else {
self.call_stack.push(frame);
true
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn upcall_scope<'a>(
&'a mut self,
reg: Register,
is_function: bool,
constants: &'a [ConstantDatum],
heap: &'a mut Heap,
arg_linecols: &'a [LineCol],
last_error: &'a Option<(LineCol, String)>,
data: &'a [Option<ConstantDatum>],
) -> Scope<'a> {
let (is_global, index) = reg.to_parts();
assert!(!is_global);
let index = usize::from(index);
Scope {
regs: &mut self.regs,
constants,
heap,
fp: self.fp + index,
arg_offset: if is_function { 1 } else { 0 },
arg_linecols,
last_error,
data,
}
}
pub(super) fn clear_runtime_state(&mut self) {
self.pc = 0;
self.regs.fill(0);
self.fp = usize::from(Register::MAX_GLOBAL);
self.stop = None;
self.err_handler = ErrorHandler::None;
self.call_stack.clear();
self.yield_pending = false;
}
pub(super) fn exec(&mut self, image: &Image, heap: &mut Heap) -> InternalStopReason {
while self.stop.is_none() {
if self.yield_pending && image.debug_info.instrs[self.pc].is_stmt_start {
self.yield_pending = false;
self.stop = Some(InternalStopReason::Yield);
continue;
}
let instr = image.code[self.pc];
match opcode_of(instr) {
Opcode::AddDouble => self.do_add_double(instr),
Opcode::AddInteger => self.do_add_integer(instr),
Opcode::Alloc => self.do_alloc(instr, heap),
Opcode::AllocArray => self.do_alloc_array(instr, heap),
Opcode::BitwiseAnd => self.do_bitwise_and(instr),
Opcode::BitwiseNot => self.do_bitwise_not(instr),
Opcode::BitwiseOr => self.do_bitwise_or(instr),
Opcode::BitwiseXor => self.do_bitwise_xor(instr),
Opcode::Call => self.do_call(instr),
Opcode::Concat => self.do_concat(instr, &image.constants, heap),
Opcode::DivideDouble => self.do_divide_double(instr),
Opcode::DivideInteger => self.do_divide_integer(instr),
Opcode::DoubleToInteger => self.do_double_to_integer(instr),
Opcode::EqualBoolean => self.do_equal_boolean(instr),
Opcode::EqualDouble => self.do_equal_double(instr),
Opcode::EqualInteger => self.do_equal_integer(instr),
Opcode::EqualText => self.do_equal_text(instr, &image.constants, heap),
Opcode::End => self.do_end(instr),
Opcode::Eof => self.do_eof(instr),
Opcode::Gosub => self.do_gosub(instr),
Opcode::GreaterDouble => self.do_greater_double(instr),
Opcode::GreaterEqualDouble => self.do_greater_equal_double(instr),
Opcode::GreaterEqualInteger => self.do_greater_equal_integer(instr),
Opcode::GreaterEqualText => {
self.do_greater_equal_text(instr, &image.constants, heap)
}
Opcode::GreaterInteger => self.do_greater_integer(instr),
Opcode::GreaterText => self.do_greater_text(instr, &image.constants, heap),
Opcode::IntegerToDouble => self.do_integer_to_double(instr),
Opcode::Jump => self.do_jump(instr),
Opcode::JumpIfFalse => self.do_jump_if_false(instr),
Opcode::LessDouble => self.do_less_double(instr),
Opcode::LessEqualDouble => self.do_less_equal_double(instr),
Opcode::LessEqualInteger => self.do_less_equal_integer(instr),
Opcode::LessEqualText => self.do_less_equal_text(instr, &image.constants, heap),
Opcode::LessInteger => self.do_less_integer(instr),
Opcode::LessText => self.do_less_text(instr, &image.constants, heap),
Opcode::LoadArray => self.do_load_array(instr, heap),
Opcode::LoadConstant => self.do_load_constant(instr, &image.constants),
Opcode::LoadInteger => self.do_load_integer(instr),
Opcode::LoadRegisterPointer => self.do_load_register_ptr(instr),
Opcode::ModuloDouble => self.do_modulo_double(instr),
Opcode::ModuloInteger => self.do_modulo_integer(instr),
Opcode::Move => self.do_move(instr),
Opcode::MultiplyDouble => self.do_multiply_double(instr),
Opcode::MultiplyInteger => self.do_multiply_integer(instr),
Opcode::NegateDouble => self.do_negate_double(instr),
Opcode::NegateInteger => self.do_negate_integer(instr),
Opcode::NotEqualBoolean => self.do_not_equal_boolean(instr),
Opcode::NotEqualDouble => self.do_not_equal_double(instr),
Opcode::NotEqualInteger => self.do_not_equal_integer(instr),
Opcode::NotEqualText => self.do_not_equal_text(instr, &image.constants, heap),
Opcode::Nop => self.do_nop(instr),
Opcode::PowerDouble => self.do_power_double(instr),
Opcode::PowerInteger => self.do_power_integer(instr),
Opcode::Return => self.do_return(instr),
Opcode::SetErrorHandler => self.do_set_error_handler(instr),
Opcode::ShiftLeft => self.do_shift_left(instr),
Opcode::ShiftRight => self.do_shift_right(instr),
Opcode::StoreArray => self.do_store_array(instr, heap),
Opcode::SubtractDouble => self.do_subtract_double(instr),
Opcode::SubtractInteger => self.do_subtract_integer(instr),
Opcode::Upcall => self.do_upcall(instr),
Opcode::UpcallAsync => self.do_upcall_async(instr),
}
}
self.stop.take().expect("The loop above can only exit when there is a stop reason")
}
}
impl Context {
fn do_binary_double_op<F>(
&mut self,
instr: u32,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(f64, f64) -> f64,
{
let (dest, src1, src2) = parse(instr);
let lhs = f64::from_bits(self.get_reg(src1));
let rhs = f64::from_bits(self.get_reg(src2));
self.set_reg(dest, op(lhs, rhs).to_bits());
self.pc += 1;
}
fn do_binary_double_predicate_op<F>(
&mut self,
instr: u32,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(f64, f64) -> bool,
{
let (dest, src1, src2) = parse(instr);
let lhs = f64::from_bits(self.get_reg(src1));
let rhs = f64::from_bits(self.get_reg(src2));
self.set_reg(dest, if op(lhs, rhs) { 1 } else { 0 });
self.pc += 1;
}
fn do_binary_integer_op<F, E>(
&mut self,
instr: u32,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(i32, i32) -> Result<i32, E>,
E: ToString,
{
let (dest, src1, src2) = parse(instr);
let lhs = self.get_reg(src1) as i32;
let rhs = self.get_reg(src2) as i32;
match op(lhs, rhs) {
Ok(result) => {
self.set_reg(dest, result as u64);
self.pc += 1;
}
Err(msg) => {
self.set_exception(msg.to_string());
}
}
}
fn do_binary_integer_predicate_op<F>(
&mut self,
instr: u32,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(i32, i32) -> bool,
{
let (dest, src1, src2) = parse(instr);
let lhs = self.get_reg(src1) as i32;
let rhs = self.get_reg(src2) as i32;
self.set_reg(dest, if op(lhs, rhs) { 1 } else { 0 });
self.pc += 1;
}
fn do_binary_boolean_op<F>(
&mut self,
instr: u32,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(bool, bool) -> bool,
{
let (dest, src1, src2) = parse(instr);
let lhs = self.get_reg(src1) != 0;
let rhs = self.get_reg(src2) != 0;
self.set_reg(dest, if op(lhs, rhs) { 1 } else { 0 });
self.pc += 1;
}
fn do_binary_text_op<F>(
&mut self,
instr: u32,
constants: &[ConstantDatum],
heap: &Heap,
parse: fn(u32) -> (Register, Register, Register),
op: F,
) where
F: Fn(&str, &str) -> bool,
{
let (dest, src1, src2) = parse(instr);
let lhs = self.deref_string(src1, constants, heap);
let rhs = self.deref_string(src2, constants, heap);
self.set_reg(dest, if op(lhs, rhs) { 1 } else { 0 });
self.pc += 1;
}
pub(super) fn do_add_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_add_double, |l, r| l + r);
}
pub(super) fn do_add_integer(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_add_integer, checked_add_integer);
}
pub(super) fn do_alloc(&mut self, instr: u32, heap: &mut Heap) {
let (dest, etype) = bytecode::parse_alloc(instr);
debug_assert_eq!(ExprType::Text, etype, "Alloc is only emitted for strings right now");
self.set_reg(dest, heap.empty_text_ptr());
self.pc += 1;
}
pub(super) fn do_alloc_array(&mut self, instr: u32, heap: &mut Heap) {
let (dest, packed, first_dim_reg) = bytecode::parse_alloc_array(instr);
let subtype = packed.subtype();
let ndims = usize::from(packed.ndims());
let (_, first_idx) = first_dim_reg.to_parts();
let mut dimensions = Vec::with_capacity(ndims);
let mut total: usize = 1;
for i in 0..ndims {
let dim_reg = Register::local(first_idx + i as u8).unwrap();
let dim = match usize::try_from(self.get_reg(dim_reg) as i32) {
Ok(0) | Err(_) => {
self.set_exception(format!("Dimension {} must be positive", i));
return;
}
Ok(n) => n,
};
dimensions.push(dim);
total *= dim;
}
let values = match subtype {
ExprType::Boolean | ExprType::Double | ExprType::Integer => {
vec![0; total]
}
ExprType::Text => vec![heap.empty_text_ptr(); total],
};
let array = ArrayData { dimensions, values };
let ptr = match heap.push(HeapDatum::Array(array)) {
Ok(ptr) => ptr,
Err(e) => {
self.set_exception(e.to_string());
return;
}
};
self.set_reg(dest, ptr);
self.pc += 1;
}
pub(super) fn do_bitwise_and(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_bitwise_and, checked_and_integer);
}
pub(super) fn do_bitwise_not(&mut self, instr: u32) {
let reg = bytecode::parse_bitwise_not(instr);
let value = self.get_reg(reg) as i32;
self.set_reg(reg, (!value) as u64);
self.pc += 1;
}
pub(super) fn do_bitwise_or(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_bitwise_or, checked_or_integer);
}
pub(super) fn do_bitwise_xor(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_bitwise_xor, checked_xor_integer);
}
pub(super) fn do_call(&mut self, instr: u32) {
let (reg, offset) = bytecode::parse_call(instr);
if !self.push_frame(Frame { old_pc: self.pc, old_fp: self.fp, ret_reg: Some(reg) }) {
return;
}
self.pc = Address::from(offset);
let (is_global, index) = reg.to_parts();
debug_assert!(!is_global, "Function results are always stored to a temp register");
self.fp += usize::from(index);
}
pub(super) fn do_concat(&mut self, instr: u32, constants: &[ConstantDatum], heap: &mut Heap) {
let (dest, src1, src2) = bytecode::parse_concat(instr);
let lhs = self.deref_string(src1, constants, heap).to_owned();
let rhs = self.deref_string(src2, constants, heap);
let result = lhs + rhs;
let ptr = match heap.push(HeapDatum::Text(result)) {
Ok(ptr) => ptr,
Err(e) => {
self.set_exception(e.to_string());
return;
}
};
self.set_reg(dest, ptr);
self.pc += 1;
}
pub(super) fn do_divide_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_divide_double, |l, r| l / r);
}
pub(super) fn do_divide_integer(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_divide_integer, checked_div_integer);
}
pub(super) fn do_double_to_integer(&mut self, instr: u32) {
let reg = bytecode::parse_double_to_integer(instr);
let dvalue = f64::from_bits(self.get_reg(reg)).round();
if dvalue.is_finite() && dvalue >= (i32::MIN as f64) && dvalue <= (i32::MAX as f64) {
self.set_reg(reg, (dvalue as i32) as u64);
} else {
self.set_exception(format!("Cannot cast {} to integer due to overflow", dvalue));
return;
}
self.pc += 1;
}
pub(super) fn do_equal_boolean(&mut self, instr: u32) {
self.do_binary_boolean_op(instr, bytecode::parse_equal_boolean, |l, r| l == r);
}
pub(super) fn do_equal_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_equal_double, |l, r| l == r);
}
pub(super) fn do_equal_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(instr, bytecode::parse_equal_integer, |l, r| l == r);
}
pub(super) fn do_equal_text(&mut self, instr: u32, constants: &[ConstantDatum], heap: &Heap) {
self.do_binary_text_op(instr, constants, heap, bytecode::parse_equal_text, |l, r| l == r);
}
pub(super) fn do_end(&mut self, instr: u32) {
let reg = bytecode::parse_end(instr);
let code = self.get_reg(reg) as i32;
let code = match ExitCode::try_from(code) {
Ok(code) => code,
Err(e) => {
self.set_exception(e.to_string());
return;
}
};
self.stop = Some(InternalStopReason::End(code));
}
pub(super) fn do_eof(&mut self, instr: u32) {
bytecode::parse_eof(instr);
self.stop = Some(InternalStopReason::Eof);
}
pub(super) fn do_gosub(&mut self, instr: u32) {
let offset = bytecode::parse_gosub(instr);
if !self.push_frame(Frame { old_pc: self.pc, old_fp: self.fp, ret_reg: None }) {
return;
}
self.pc = Address::from(offset);
}
pub(super) fn do_greater_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_greater_double, |l, r| l > r);
}
pub(super) fn do_greater_equal_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_greater_equal_double, |l, r| {
l >= r
});
}
pub(super) fn do_greater_equal_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(
instr,
bytecode::parse_greater_equal_integer,
|l, r| l >= r,
);
}
pub(super) fn do_greater_equal_text(
&mut self,
instr: u32,
constants: &[ConstantDatum],
heap: &Heap,
) {
self.do_binary_text_op(
instr,
constants,
heap,
bytecode::parse_greater_equal_text,
|l, r| l >= r,
);
}
pub(super) fn do_greater_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(instr, bytecode::parse_greater_integer, |l, r| l > r);
}
pub(super) fn do_greater_text(&mut self, instr: u32, constants: &[ConstantDatum], heap: &Heap) {
self.do_binary_text_op(instr, constants, heap, bytecode::parse_greater_text, |l, r| l > r);
}
pub(super) fn do_integer_to_double(&mut self, instr: u32) {
let reg = bytecode::parse_integer_to_double(instr);
let ivalue = self.get_reg(reg) as i32;
self.set_reg(reg, (ivalue as f64).to_bits());
self.pc += 1;
}
pub(super) fn do_jump(&mut self, instr: u32) {
let old_pc = self.pc;
let offset = bytecode::parse_jump(instr);
self.pc = Address::from(offset);
if self.pc <= old_pc {
self.yield_pending = true;
}
}
pub(super) fn do_jump_if_false(&mut self, instr: u32) {
let (cond_reg, target) = bytecode::parse_jump_if_false(instr);
if self.get_reg(cond_reg) != 0 {
self.pc += 1;
} else {
let old_pc = self.pc;
self.pc = Address::from(target);
if self.pc <= old_pc {
self.yield_pending = true;
}
}
}
pub(super) fn do_less_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_less_double, |l, r| l < r);
}
pub(super) fn do_less_equal_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_less_equal_double, |l, r| l <= r);
}
pub(super) fn do_less_equal_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(instr, bytecode::parse_less_equal_integer, |l, r| {
l <= r
});
}
pub(super) fn do_less_equal_text(
&mut self,
instr: u32,
constants: &[ConstantDatum],
heap: &Heap,
) {
self.do_binary_text_op(instr, constants, heap, bytecode::parse_less_equal_text, |l, r| {
l <= r
});
}
pub(super) fn do_less_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(instr, bytecode::parse_less_integer, |l, r| l < r);
}
pub(super) fn do_less_text(&mut self, instr: u32, constants: &[ConstantDatum], heap: &Heap) {
self.do_binary_text_op(instr, constants, heap, bytecode::parse_less_text, |l, r| l < r);
}
pub(super) fn do_load_array(&mut self, instr: u32, heap: &Heap) {
let (dest, arr_reg, first_sub_reg) = bytecode::parse_load_array(instr);
if let Some((heap_idx, flat_idx)) = self.resolve_array_index(arr_reg, first_sub_reg, heap) {
let array = match heap.get(heap_idx) {
HeapDatum::Array(a) => a,
_ => unreachable!("Register must point to an array"),
};
self.set_reg(dest, array.values[flat_idx]);
self.pc += 1;
}
}
pub(super) fn do_load_constant(&mut self, instr: u32, constants: &[ConstantDatum]) {
let (register, i) = bytecode::parse_load_constant(instr);
match &constants[usize::from(i)] {
ConstantDatum::Boolean(_) => unreachable!("Booleans are always immediates"),
ConstantDatum::Double(d) => self.set_reg(register, d.to_bits()),
ConstantDatum::Integer(i) => self.set_reg(register, *i as u64),
ConstantDatum::Text(_) => unreachable!("Strings cannot be loaded into registers"),
}
self.pc += 1;
}
pub(super) fn do_load_integer(&mut self, instr: u32) {
let (register, i) = bytecode::parse_load_integer(instr);
self.set_reg(register, i as u64);
self.pc += 1;
}
pub(super) fn do_load_register_ptr(&mut self, instr: u32) {
let (dest, vtype, src) = bytecode::parse_load_register_ptr(instr);
let tagged_ref = TaggedRegisterRef::new(src, self.fp, vtype);
self.set_reg(dest, tagged_ref.as_u64());
self.pc += 1;
}
pub(super) fn do_modulo_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_modulo_double, |l, r| l % r);
}
pub(super) fn do_modulo_integer(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_modulo_integer, checked_mod_integer);
}
pub(super) fn do_move(&mut self, instr: u32) {
let (dest, src) = bytecode::parse_move(instr);
let value = self.get_reg(src);
self.set_reg(dest, value);
self.pc += 1;
}
pub(super) fn do_multiply_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_multiply_double, |l, r| l * r);
}
pub(super) fn do_multiply_integer(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_multiply_integer, checked_mul_integer);
}
pub(super) fn do_negate_double(&mut self, instr: u32) {
let reg = bytecode::parse_negate_double(instr);
let value = f64::from_bits(self.get_reg(reg));
self.set_reg(reg, (-value).to_bits());
self.pc += 1;
}
pub(super) fn do_negate_integer(&mut self, instr: u32) {
let reg = bytecode::parse_negate_integer(instr);
let value = self.get_reg(reg) as i32;
match value.checked_neg() {
Some(result) => {
self.set_reg(reg, result as u64);
self.pc += 1;
}
None => {
self.set_exception("Integer overflow");
}
}
}
pub(super) fn do_not_equal_boolean(&mut self, instr: u32) {
self.do_binary_boolean_op(instr, bytecode::parse_not_equal_boolean, |l, r| l != r);
}
pub(super) fn do_not_equal_double(&mut self, instr: u32) {
self.do_binary_double_predicate_op(instr, bytecode::parse_not_equal_double, |l, r| l != r);
}
pub(super) fn do_not_equal_integer(&mut self, instr: u32) {
self.do_binary_integer_predicate_op(instr, bytecode::parse_not_equal_integer, |l, r| {
l != r
});
}
pub(super) fn do_not_equal_text(
&mut self,
instr: u32,
constants: &[ConstantDatum],
heap: &Heap,
) {
self.do_binary_text_op(instr, constants, heap, bytecode::parse_not_equal_text, |l, r| {
l != r
});
}
pub(super) fn do_nop(&mut self, instr: u32) {
bytecode::parse_nop(instr);
self.pc += 1;
}
pub(super) fn do_power_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_power_double, |l, r| l.powf(r));
}
pub(super) fn do_power_integer(&mut self, instr: u32) {
let (dest, src1, src2) = bytecode::parse_power_integer(instr);
let lhs = self.get_reg(src1) as i32;
let rhs = self.get_reg(src2) as i32;
let exp = match u32::try_from(rhs) {
Ok(exp) => exp,
Err(_) => {
self.set_exception(format!("Exponent {} cannot be negative", rhs));
return;
}
};
match checked_pow_integer(lhs, exp) {
Ok(result) => {
self.set_reg(dest, result as u64);
self.pc += 1;
}
Err(msg) => {
self.set_exception(msg);
}
}
}
pub(super) fn do_return(&mut self, instr: u32) {
bytecode::parse_return(instr);
let frame = match self.call_stack.pop() {
Some(frame) => frame,
None => {
self.set_exception("RETURN without GOSUB or FUNCTION call");
return;
}
};
if let Some(ret_reg) = frame.ret_reg {
let return_value = self.get_reg(Register::local(0).unwrap());
self.pc = frame.old_pc + 1;
self.fp = frame.old_fp;
self.set_reg(ret_reg, return_value);
} else {
self.pc = frame.old_pc + 1;
self.fp = frame.old_fp;
self.yield_pending = true;
}
}
pub(super) fn do_set_error_handler(&mut self, instr: u32) {
let (mode, target) = bytecode::parse_set_error_handler(instr);
self.err_handler = match mode {
ErrorHandlerMode::None => ErrorHandler::None,
ErrorHandlerMode::Jump => {
ErrorHandler::Jump { active: false, addr: usize::from(target) }
}
ErrorHandlerMode::ResumeNext => ErrorHandler::ResumeNext,
};
self.pc += 1;
}
pub(super) fn do_shift_left(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_shift_left, checked_shl_integer);
}
pub(super) fn do_shift_right(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_shift_right, checked_shr_integer);
}
pub(super) fn do_store_array(&mut self, instr: u32, heap: &mut Heap) {
let (arr_reg, val_reg, first_sub_reg) = bytecode::parse_store_array(instr);
let value = self.get_reg(val_reg);
if let Some((heap_idx, flat_idx)) = self.resolve_array_index(arr_reg, first_sub_reg, heap) {
let array = match heap.get_mut(heap_idx) {
HeapDatum::Array(a) => a,
_ => unreachable!("Register must point to an array"),
};
array.values[flat_idx] = value;
self.pc += 1;
}
}
pub(super) fn do_subtract_double(&mut self, instr: u32) {
self.do_binary_double_op(instr, bytecode::parse_subtract_double, |l, r| l - r);
}
pub(super) fn do_subtract_integer(&mut self, instr: u32) {
self.do_binary_integer_op(instr, bytecode::parse_subtract_integer, checked_sub_integer);
}
pub(super) fn do_upcall(&mut self, instr: u32) {
let (index, first_reg) = bytecode::parse_upcall(instr);
let upcall_pc = self.pc;
self.pc += 1;
self.stop = Some(InternalStopReason::Upcall(index, first_reg, upcall_pc));
}
pub(super) fn do_upcall_async(&mut self, instr: u32) {
let (index, first_reg) = bytecode::parse_upcall_async(instr);
let upcall_pc = self.pc;
self.pc += 1;
self.stop = Some(InternalStopReason::UpcallAsync(index, first_reg, upcall_pc));
}
}