use std::{any, any::TypeId, marker};
use gazebo::dupe::Dupe;
use crate::eval::bc::{instr::BcInstr, instr_impl::*};
#[starlark_internal_bc]
#[derive(Debug, Copy, Clone, Dupe, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u32)]
pub(crate) enum BcOpcode {
Dup,
Pop,
Const,
Const2,
Const3,
Const4,
LoadLocal,
LoadLocal2,
LoadLocal3,
LoadLocal4,
LoadLocalAndConst,
LoadLocalCaptured,
LoadModule,
StoreLocal,
StoreLocalCaptured,
StoreModule,
StoreModuleAndExport,
Unpack,
ArrayIndex,
ArrayIndexNoPop,
SetArrayIndex,
ArrayIndexSet,
Slice,
ObjectField,
SetObjectField,
ObjectSetField,
Eq,
NotEq,
Not,
Minus,
Plus,
BitNot,
Less,
Greater,
LessOrEqual,
GreaterOrEqual,
In,
NotIn,
Add,
AddAssign,
Sub,
Multiply,
Percent,
PercentSOne,
FormatOne,
Divide,
FloorDivide,
BitAnd,
BitOr,
BitOrAssign,
BitXor,
LeftShift,
RightShift,
Len,
Type,
TypeIs,
TupleNPop,
ListNew,
ListNPop,
ListOfConsts,
DictNew,
DictNPop,
DictOfConsts,
DictConstKeys,
ComprListAppend,
ComprDictInsert,
Br,
IfBr,
IfNotBr,
ForLoop,
Break,
Continue,
Return,
ReturnConst,
Call,
CallPos,
CallFrozenDef,
CallFrozenDefPos,
CallFrozenNative,
CallFrozenNativePos,
CallFrozen,
CallFrozenPos,
CallMethod,
CallMethodPos,
CallMaybeKnownMethod,
CallMaybeKnownMethodPos,
Def,
PossibleGc,
BeforeStmt,
ProfileBc,
End,
}
pub(crate) trait BcOpcodeHandler<R> {
fn handle<I: BcInstr>(self) -> R;
}
pub(crate) trait BcOpcodeAllHandler {
fn handle<I: BcInstr>(&mut self, opcode: BcOpcode);
}
impl BcOpcode {
pub(crate) const COUNT: usize = (BcOpcode::End as usize) + 1;
#[cfg_attr(not(debug_assertions), inline(always))]
pub(crate) fn dispatch<R>(self, handler: impl BcOpcodeHandler<R>) -> R {
self.do_dispatch(handler)
}
#[inline(always)]
pub(crate) fn dispatch_all(handler: &mut impl BcOpcodeAllHandler) {
BcOpcode::do_dispatch_all(handler)
}
pub(crate) fn by_number(n: u32) -> Option<BcOpcode> {
struct ByNumber {
i: u32,
n: u32,
opcode: Option<BcOpcode>,
}
impl BcOpcodeAllHandler for ByNumber {
fn handle<I: BcInstr>(&mut self, opcode: BcOpcode) {
if self.i == self.n {
self.opcode = Some(opcode);
}
self.i += 1;
}
}
let mut by_number = ByNumber {
i: 0,
n,
opcode: None,
};
BcOpcode::do_dispatch_all(&mut by_number);
by_number.opcode
}
pub(crate) fn for_instr<I: BcInstr>() -> BcOpcode {
struct FindOpcode<I: BcInstr> {
opcode: Option<BcOpcode>,
_marker: marker::PhantomData<I>,
}
impl<I: BcInstr> BcOpcodeAllHandler for FindOpcode<I> {
fn handle<J: BcInstr>(&mut self, opcode: BcOpcode) {
if TypeId::of::<I>() == TypeId::of::<J>() {
assert!(self.opcode.is_none());
self.opcode = Some(opcode);
}
}
}
let mut find_opcode = FindOpcode::<I> {
opcode: None,
_marker: marker::PhantomData,
};
Self::dispatch_all(&mut find_opcode);
find_opcode
.opcode
.unwrap_or_else(|| panic!("No opcode for instruction {:?}", any::type_name::<I>()))
}
}
#[cfg(test)]
mod tests {
use crate::eval::bc::opcode::BcOpcode;
#[test]
fn opcode_count() {
for i in 0..10000 {
if i < (BcOpcode::COUNT as u32) {
assert!(BcOpcode::by_number(i).is_some());
} else {
assert!(BcOpcode::by_number(i).is_none());
}
}
}
}