use std::fmt::Write;
use starlark_syntax::eval_exception::EvalException;
use crate::eval::bc::addr::BcPtrAddr;
use crate::eval::bc::for_loop::LoopDepth;
use crate::eval::bc::frame::BcFramePtr;
use crate::eval::bc::instr::BcInstr;
use crate::eval::bc::instr::InstrControl;
use crate::eval::bc::instr_impl::InstrEnd;
use crate::eval::bc::instrs::BcInstrs;
use crate::eval::bc::opcode::BcOpcode;
use crate::eval::bc::opcode::BcOpcodeHandler;
use crate::eval::bc::slow_arg::BcInstrEndArg;
use crate::eval::bc::slow_arg::BcInstrSlowArg;
use crate::eval::compiler::add_span_to_expr_error;
use crate::eval::runtime::evaluator::EvaluationCallbacks;
use crate::eval::Evaluator;
use crate::values::Value;
#[derive(Default)]
pub(crate) struct Bc {
pub(crate) instrs: BcInstrs,
pub(crate) local_count: u32,
pub(crate) max_stack_size: u32,
pub(crate) max_loop_depth: LoopDepth,
}
impl Bc {
#[cold]
#[inline(never)]
pub(crate) fn slow_arg_at_ptr(addr_ptr: BcPtrAddr) -> &BcInstrSlowArg {
let mut ptr = addr_ptr;
loop {
let opcode = ptr.get_opcode();
if opcode == BcOpcode::End {
let end_of_bc = ptr.get_instr::<InstrEnd>();
let BcInstrEndArg {
slow_args,
end_addr,
..
} = &end_of_bc.arg;
let code_start_ptr = ptr.sub(*end_addr);
let addr = addr_ptr.offset_from(code_start_ptr);
for (next_addr, next_span) in slow_args {
if *next_addr == addr {
return next_span;
}
}
panic!("span not found for addr: {}", addr);
}
ptr = ptr.add(opcode.size_of_repr());
}
}
#[cold]
#[inline(never)]
pub(crate) fn wrap_error_for_instr_ptr(
ptr: BcPtrAddr,
e: crate::Error,
eval: &Evaluator,
) -> EvalException {
let span = Self::slow_arg_at_ptr(ptr).span;
add_span_to_expr_error(e, span, eval)
}
#[inline(always)]
pub(crate) fn run<'v, EC: EvaluationCallbacks>(
&self,
eval: &mut Evaluator<'v, '_, '_>,
ec: &mut EC,
) -> Result<Value<'v>, EvalException> {
debug_assert!(eval.current_frame.is_inititalized());
debug_assert_eq!(self.max_stack_size, eval.current_frame.max_stack_size());
run_block(eval, ec, self.instrs.start_ptr())
}
pub(crate) fn dump_debug(&self) -> String {
let mut w = String::new();
writeln!(w, "Max stack size: {}", self.max_stack_size).unwrap();
writeln!(w, "Instructions:").unwrap();
self.instrs
.dump_debug()
.lines()
.for_each(|line| writeln!(w, " {}", line).unwrap());
w
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn step<'v, 'b, EC: EvaluationCallbacks>(
eval: &mut Evaluator<'v, '_, '_>,
ec: &mut EC,
frame: BcFramePtr<'v>,
ip: BcPtrAddr<'b>,
) -> InstrControl<'v, 'b> {
let opcode = ip.get_opcode();
struct HandlerImpl<'v, 'a, 'e, 'y, 'b> {
eval: &'y mut Evaluator<'v, 'a, 'e>,
frame: BcFramePtr<'v>,
ip: BcPtrAddr<'b>,
}
impl<'v, 'a, 'e, 'y, 'b> BcOpcodeHandler<InstrControl<'v, 'b>> for HandlerImpl<'v, 'a, 'e, 'y, 'b> {
#[cfg_attr(not(debug_assertions), inline(always))]
fn handle<I: BcInstr>(self) -> InstrControl<'v, 'b> {
let HandlerImpl { eval, frame, ip } = self;
let repr = ip.get_instr::<I>();
I::run(eval, frame, ip, &repr.arg)
}
}
match ec.before_instr(eval, ip, opcode) {
Ok(()) => {}
Err(e) => return InstrControl::Err(e),
}
opcode.dispatch(HandlerImpl { eval, frame, ip })
}
pub(crate) fn run_block<'v, EC: EvaluationCallbacks>(
eval: &mut Evaluator<'v, '_, '_>,
ec: &mut EC,
mut ip: BcPtrAddr,
) -> Result<Value<'v>, EvalException> {
let frame = eval.current_frame;
loop {
ip = match step(eval, ec, frame, ip) {
InstrControl::Next(ip) => ip,
InstrControl::Return(v) => return Ok(v),
InstrControl::Err(e) => return Err(Bc::wrap_error_for_instr_ptr(ip, e, eval)),
}
}
}