Files
RustPython/crates/compiler-core/src/bytecode/instruction.rs
T

1295 lines
48 KiB
Rust

use core::{fmt, marker::PhantomData, mem};
use crate::{
bytecode::{
BorrowedConstant, Constant, InstrDisplayContext,
oparg::{
BinaryOperator, BuildSliceArgCount, CommonConstant, ComparisonOperator,
ConvertValueOparg, IntrinsicFunction1, IntrinsicFunction2, Invert, Label, LoadAttr,
LoadSuperAttr, MakeFunctionFlags, NameIdx, OpArg, OpArgByte, OpArgType, RaiseKind,
SpecialMethod, UnpackExArgs,
},
},
marshal::MarshalError,
};
/// A Single bytecode instruction that are executed by the VM.
///
/// Currently aligned with CPython 3.14.
///
/// ## See also
/// - [CPython opcode IDs](https://github.com/python/cpython/blob/v3.14.2/Include/opcode_ids.h)
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum Instruction {
// No-argument instructions (opcode < HAVE_ARGUMENT=44)
Cache = 0,
BinarySlice = 1,
BuildTemplate = 2,
BinaryOpInplaceAddUnicode = 3,
CallFunctionEx = 4,
CheckEgMatch = 5,
CheckExcMatch = 6,
CleanupThrow = 7,
DeleteSubscr = 8,
EndFor = 9,
EndSend = 10,
ExitInitCheck = 11, // Placeholder
FormatSimple = 12,
FormatWithSpec = 13,
GetAIter = 14,
GetANext = 15,
GetIter = 16,
Reserved = 17,
GetLen = 18,
GetYieldFromIter = 19,
InterpreterExit = 20, // Placeholder
LoadBuildClass = 21,
LoadLocals = 22,
MakeFunction = 23,
MatchKeys = 24,
MatchMapping = 25,
MatchSequence = 26,
Nop = 27,
NotTaken = 28,
PopExcept = 29,
PopIter = 30,
PopTop = 31,
PushExcInfo = 32,
PushNull = 33,
ReturnGenerator = 34,
ReturnValue = 35,
SetupAnnotations = 36,
StoreSlice = 37,
StoreSubscr = 38,
ToBool = 39,
UnaryInvert = 40,
UnaryNegative = 41,
UnaryNot = 42,
WithExceptStart = 43,
// CPython 3.14 opcodes with arguments (44-120)
BinaryOp {
op: Arg<BinaryOperator>,
} = 44,
/// Build an Interpolation from value, expression string, and optional format_spec on stack.
///
/// oparg encoding: (conversion << 2) | has_format_spec
/// - has_format_spec (bit 0): if 1, format_spec is on stack
/// - conversion (bits 2+): 0=None, 1=Str, 2=Repr, 3=Ascii
///
/// Stack: [value, expression_str, format_spec?] -> [interpolation]
BuildInterpolation {
oparg: Arg<u32>,
} = 45,
BuildList {
size: Arg<u32>,
} = 46,
BuildMap {
size: Arg<u32>,
} = 47,
BuildSet {
size: Arg<u32>,
} = 48,
BuildSlice {
argc: Arg<BuildSliceArgCount>,
} = 49,
BuildString {
size: Arg<u32>,
} = 50,
BuildTuple {
size: Arg<u32>,
} = 51,
Call {
nargs: Arg<u32>,
} = 52,
CallIntrinsic1 {
func: Arg<IntrinsicFunction1>,
} = 53,
CallIntrinsic2 {
func: Arg<IntrinsicFunction2>,
} = 54,
CallKw {
nargs: Arg<u32>,
} = 55,
CompareOp {
op: Arg<ComparisonOperator>,
} = 56,
ContainsOp(Arg<Invert>) = 57,
ConvertValue {
oparg: Arg<ConvertValueOparg>,
} = 58,
Copy {
index: Arg<u32>,
} = 59,
CopyFreeVars {
count: Arg<u32>,
} = 60,
DeleteAttr {
idx: Arg<NameIdx>,
} = 61,
DeleteDeref(Arg<NameIdx>) = 62,
DeleteFast(Arg<NameIdx>) = 63,
DeleteGlobal(Arg<NameIdx>) = 64,
DeleteName(Arg<NameIdx>) = 65,
DictMerge {
index: Arg<u32>,
} = 66,
DictUpdate {
index: Arg<u32>,
} = 67,
EndAsyncFor = 68,
ExtendedArg = 69,
ForIter {
target: Arg<Label>,
} = 70,
GetAwaitable {
arg: Arg<u32>,
} = 71,
ImportFrom {
idx: Arg<NameIdx>,
} = 72,
ImportName {
idx: Arg<NameIdx>,
} = 73,
IsOp(Arg<Invert>) = 74,
JumpBackward {
target: Arg<Label>,
} = 75,
JumpBackwardNoInterrupt {
target: Arg<Label>,
} = 76, // Placeholder
JumpForward {
target: Arg<Label>,
} = 77,
ListAppend {
i: Arg<u32>,
} = 78,
ListExtend {
i: Arg<u32>,
} = 79,
LoadAttr {
idx: Arg<LoadAttr>,
} = 80,
LoadCommonConstant {
idx: Arg<CommonConstant>,
} = 81,
LoadConst {
idx: Arg<u32>,
} = 82,
LoadDeref(Arg<NameIdx>) = 83,
LoadFast(Arg<NameIdx>) = 84,
LoadFastAndClear(Arg<NameIdx>) = 85,
LoadFastBorrow(Arg<NameIdx>) = 86,
LoadFastBorrowLoadFastBorrow {
arg: Arg<u32>,
} = 87,
LoadFastCheck(Arg<NameIdx>) = 88,
LoadFastLoadFast {
arg: Arg<u32>,
} = 89,
LoadFromDictOrDeref(Arg<NameIdx>) = 90,
LoadFromDictOrGlobals(Arg<NameIdx>) = 91,
LoadGlobal(Arg<NameIdx>) = 92,
LoadName(Arg<NameIdx>) = 93,
LoadSmallInt {
idx: Arg<u32>,
} = 94,
LoadSpecial {
method: Arg<SpecialMethod>,
} = 95,
LoadSuperAttr {
arg: Arg<LoadSuperAttr>,
} = 96,
MakeCell(Arg<NameIdx>) = 97,
MapAdd {
i: Arg<u32>,
} = 98,
MatchClass(Arg<u32>) = 99,
PopJumpIfFalse {
target: Arg<Label>,
} = 100,
PopJumpIfNone {
target: Arg<Label>,
} = 101,
PopJumpIfNotNone {
target: Arg<Label>,
} = 102,
PopJumpIfTrue {
target: Arg<Label>,
} = 103,
RaiseVarargs {
kind: Arg<RaiseKind>,
} = 104,
Reraise {
depth: Arg<u32>,
} = 105,
Send {
target: Arg<Label>,
} = 106,
SetAdd {
i: Arg<u32>,
} = 107,
SetFunctionAttribute {
attr: Arg<MakeFunctionFlags>,
} = 108,
SetUpdate {
i: Arg<u32>,
} = 109,
StoreAttr {
idx: Arg<NameIdx>,
} = 110,
StoreDeref(Arg<NameIdx>) = 111,
StoreFast(Arg<NameIdx>) = 112,
StoreFastLoadFast {
store_idx: Arg<NameIdx>,
load_idx: Arg<NameIdx>,
} = 113,
StoreFastStoreFast {
arg: Arg<u32>,
} = 114,
StoreGlobal(Arg<NameIdx>) = 115,
StoreName(Arg<NameIdx>) = 116,
Swap {
index: Arg<u32>,
} = 117,
UnpackEx {
args: Arg<UnpackExArgs>,
} = 118,
UnpackSequence {
size: Arg<u32>,
} = 119,
YieldValue {
arg: Arg<u32>,
} = 120,
// CPython 3.14 RESUME (128)
Resume {
arg: Arg<u32>,
} = 128,
// CPython 3.14 specialized opcodes (129-211)
BinaryOpAddFloat = 129, // Placeholder
BinaryOpAddInt = 130, // Placeholder
BinaryOpAddUnicode = 131, // Placeholder
BinaryOpExtend = 132, // Placeholder
BinaryOpMultiplyFloat = 133, // Placeholder
BinaryOpMultiplyInt = 134, // Placeholder
BinaryOpSubscrDict = 135, // Placeholder
BinaryOpSubscrGetitem = 136, // Placeholder
BinaryOpSubscrListInt = 137, // Placeholder
BinaryOpSubscrListSlice = 138, // Placeholder
BinaryOpSubscrStrInt = 139, // Placeholder
BinaryOpSubscrTupleInt = 140, // Placeholder
BinaryOpSubtractFloat = 141, // Placeholder
BinaryOpSubtractInt = 142, // Placeholder
CallAllocAndEnterInit = 143, // Placeholder
CallBoundMethodExactArgs = 144, // Placeholder
CallBoundMethodGeneral = 145, // Placeholder
CallBuiltinClass = 146, // Placeholder
CallBuiltinFast = 147, // Placeholder
CallBuiltinFastWithKeywords = 148, // Placeholder
CallBuiltinO = 149, // Placeholder
CallIsinstance = 150, // Placeholder
CallKwBoundMethod = 151, // Placeholder
CallKwNonPy = 152, // Placeholder
CallKwPy = 153, // Placeholder
CallLen = 154, // Placeholder
CallListAppend = 155, // Placeholder
CallMethodDescriptorFast = 156, // Placeholder
CallMethodDescriptorFastWithKeywords = 157, // Placeholder
CallMethodDescriptorNoargs = 158, // Placeholder
CallMethodDescriptorO = 159, // Placeholder
CallNonPyGeneral = 160, // Placeholder
CallPyExactArgs = 161, // Placeholder
CallPyGeneral = 162, // Placeholder
CallStr1 = 163, // Placeholder
CallTuple1 = 164, // Placeholder
CallType1 = 165, // Placeholder
CompareOpFloat = 166, // Placeholder
CompareOpInt = 167, // Placeholder
CompareOpStr = 168, // Placeholder
ContainsOpDict = 169, // Placeholder
ContainsOpSet = 170, // Placeholder
ForIterGen = 171, // Placeholder
ForIterList = 172, // Placeholder
ForIterRange = 173, // Placeholder
ForIterTuple = 174, // Placeholder
JumpBackwardJit = 175, // Placeholder
JumpBackwardNoJit = 176, // Placeholder
LoadAttrClass = 177, // Placeholder
LoadAttrClassWithMetaclassCheck = 178, // Placeholder
LoadAttrGetattributeOverridden = 179, // Placeholder
LoadAttrInstanceValue = 180, // Placeholder
LoadAttrMethodLazyDict = 181, // Placeholder
LoadAttrMethodNoDict = 182, // Placeholder
LoadAttrMethodWithValues = 183, // Placeholder
LoadAttrModule = 184, // Placeholder
LoadAttrNondescriptorNoDict = 185, // Placeholder
LoadAttrNondescriptorWithValues = 186, // Placeholder
LoadAttrProperty = 187, // Placeholder
LoadAttrSlot = 188, // Placeholder
LoadAttrWithHint = 189, // Placeholder
LoadConstImmortal = 190, // Placeholder
LoadConstMortal = 191, // Placeholder
LoadGlobalBuiltin = 192, // Placeholder
LoadGlobalModule = 193, // Placeholder
LoadSuperAttrAttr = 194, // Placeholder
LoadSuperAttrMethod = 195, // Placeholder
ResumeCheck = 196, // Placeholder
SendGen = 197, // Placeholder
StoreAttrInstanceValue = 198, // Placeholder
StoreAttrSlot = 199, // Placeholder
StoreAttrWithHint = 200, // Placeholder
StoreSubscrDict = 201, // Placeholder
StoreSubscrListInt = 202, // Placeholder
ToBoolAlwaysTrue = 203, // Placeholder
ToBoolBool = 204, // Placeholder
ToBoolInt = 205, // Placeholder
ToBoolList = 206, // Placeholder
ToBoolNone = 207, // Placeholder
ToBoolStr = 208, // Placeholder
UnpackSequenceList = 209, // Placeholder
UnpackSequenceTuple = 210, // Placeholder
UnpackSequenceTwoTuple = 211, // Placeholder
// CPython 3.14 instrumented opcodes (234-254)
InstrumentedEndFor = 234, // Placeholder
InstrumentedPopIter = 235, // Placeholder
InstrumentedEndSend = 236, // Placeholder
InstrumentedForIter = 237, // Placeholder
InstrumentedInstruction = 238, // Placeholder
InstrumentedJumpForward = 239, // Placeholder
InstrumentedNotTaken = 240,
InstrumentedPopJumpIfTrue = 241, // Placeholder
InstrumentedPopJumpIfFalse = 242, // Placeholder
InstrumentedPopJumpIfNone = 243, // Placeholder
InstrumentedPopJumpIfNotNone = 244, // Placeholder
InstrumentedResume = 245, // Placeholder
InstrumentedReturnValue = 246, // Placeholder
InstrumentedYieldValue = 247, // Placeholder
InstrumentedEndAsyncFor = 248, // Placeholder
InstrumentedLoadSuperAttr = 249, // Placeholder
InstrumentedCall = 250, // Placeholder
InstrumentedCallKw = 251, // Placeholder
InstrumentedCallFunctionEx = 252, // Placeholder
InstrumentedJumpBackward = 253, // Placeholder
InstrumentedLine = 254, // Placeholder
EnterExecutor = 255, // Placeholder
}
const _: () = assert!(mem::size_of::<Instruction>() == 1);
impl From<Instruction> for u8 {
#[inline]
fn from(ins: Instruction) -> Self {
// SAFETY: there's no padding bits
unsafe { mem::transmute::<Instruction, Self>(ins) }
}
}
impl TryFrom<u8> for Instruction {
type Error = MarshalError;
#[inline]
fn try_from(value: u8) -> Result<Self, MarshalError> {
// CPython-compatible opcodes (0-120)
let cpython_start = u8::from(Self::Cache);
let cpython_end = u8::from(Self::YieldValue { arg: Arg::marker() });
// Resume has a non-contiguous opcode (128)
let resume_id = u8::from(Self::Resume { arg: Arg::marker() });
let enter_executor_id = u8::from(Self::EnterExecutor);
let specialized_start = u8::from(Self::BinaryOpAddFloat);
let specialized_end = u8::from(Self::UnpackSequenceTwoTuple);
let instrumented_start = u8::from(Self::InstrumentedEndFor);
let instrumented_end = u8::from(Self::InstrumentedLine);
// No RustPython-only opcodes anymore - all opcodes match CPython 3.14
let custom_ops: &[u8] = &[];
if (cpython_start..=cpython_end).contains(&value)
|| value == resume_id
|| value == enter_executor_id
|| custom_ops.contains(&value)
|| (specialized_start..=specialized_end).contains(&value)
|| (instrumented_start..=instrumented_end).contains(&value)
{
Ok(unsafe { mem::transmute::<u8, Self>(value) })
} else {
Err(Self::Error::InvalidBytecode)
}
}
}
impl InstructionMetadata for Instruction {
#[inline]
fn label_arg(&self) -> Option<Arg<Label>> {
match self {
Self::JumpBackward { target: l }
| Self::JumpBackwardNoInterrupt { target: l }
| Self::JumpForward { target: l }
| Self::PopJumpIfTrue { target: l }
| Self::PopJumpIfFalse { target: l }
| Self::PopJumpIfNone { target: l }
| Self::PopJumpIfNotNone { target: l }
| Self::ForIter { target: l }
| Self::Send { target: l } => Some(*l),
_ => None,
}
}
fn is_unconditional_jump(&self) -> bool {
matches!(
self,
Self::JumpForward { .. }
| Self::JumpBackward { .. }
| Self::JumpBackwardNoInterrupt { .. }
)
}
fn is_scope_exit(&self) -> bool {
matches!(
self,
Self::ReturnValue | Self::RaiseVarargs { .. } | Self::Reraise { .. }
)
}
fn stack_effect_info(&self, oparg: u32) -> StackEffect {
// Reason for converting oparg to i32 is because of expressions like `1 + (oparg -1)`
// that causes underflow errors.
let oparg = i32::try_from(oparg).expect("oparg does not fit in an `i32`");
// NOTE: Please don't "simplify" expressions here (i.e. `1 + (oparg - 1)`)
// as it will be harder to see diff with what CPython auto-generates
let (pushed, popped) = match self {
Self::BinaryOp { .. } => (1, 2),
Self::BinaryOpAddFloat => (1, 2),
Self::BinaryOpAddInt => (1, 2),
Self::BinaryOpAddUnicode => (1, 2),
Self::BinaryOpExtend => (1, 2),
Self::BinaryOpInplaceAddUnicode => (0, 2),
Self::BinaryOpMultiplyFloat => (1, 2),
Self::BinaryOpMultiplyInt => (1, 2),
Self::BinaryOpSubscrDict => (1, 2),
Self::BinaryOpSubscrGetitem => (0, 2),
Self::BinaryOpSubscrListInt => (1, 2),
Self::BinaryOpSubscrListSlice => (1, 2),
Self::BinaryOpSubscrStrInt => (1, 2),
Self::BinaryOpSubscrTupleInt => (1, 2),
Self::BinaryOpSubtractFloat => (1, 2),
Self::BinaryOpSubtractInt => (1, 2),
Self::BinarySlice { .. } => (1, 3),
Self::BuildInterpolation { .. } => (1, 2 + (oparg & 1)),
Self::BuildList { .. } => (1, oparg),
Self::BuildMap { .. } => (1, oparg * 2),
Self::BuildSet { .. } => (1, oparg),
Self::BuildSlice { .. } => (1, oparg),
Self::BuildString { .. } => (1, oparg),
Self::BuildTemplate { .. } => (1, 2),
Self::BuildTuple { .. } => (1, oparg),
Self::Cache => (0, 0),
Self::Call { .. } => (1, 2 + oparg),
Self::CallAllocAndEnterInit => (0, 2 + oparg),
Self::CallBoundMethodExactArgs => (0, 2 + oparg),
Self::CallBoundMethodGeneral => (0, 2 + oparg),
Self::CallBuiltinClass => (1, 2 + oparg),
Self::CallBuiltinFast => (1, 2 + oparg),
Self::CallBuiltinFastWithKeywords => (1, 2 + oparg),
Self::CallBuiltinO => (1, 2 + oparg),
Self::CallFunctionEx => (1, 4),
Self::CallIntrinsic1 { .. } => (1, 1),
Self::CallIntrinsic2 { .. } => (1, 2),
Self::CallIsinstance => (1, 2 + oparg),
Self::CallKw { .. } => (1, 3 + oparg),
Self::CallKwBoundMethod => (0, 3 + oparg),
Self::CallKwNonPy => (1, 3 + oparg),
Self::CallKwPy => (0, 3 + oparg),
Self::CallLen => (1, 3),
Self::CallListAppend => (0, 3),
Self::CallMethodDescriptorFast => (1, 2 + oparg),
Self::CallMethodDescriptorFastWithKeywords => (1, 2 + oparg),
Self::CallMethodDescriptorNoargs => (1, 2 + oparg),
Self::CallMethodDescriptorO => (1, 2 + oparg),
Self::CallNonPyGeneral => (1, 2 + oparg),
Self::CallPyExactArgs => (0, 2 + oparg),
Self::CallPyGeneral => (0, 2 + oparg),
Self::CallStr1 => (1, 3),
Self::CallTuple1 => (1, 3),
Self::CallType1 => (1, 3),
Self::CheckEgMatch => (2, 2),
Self::CheckExcMatch => (2, 2),
Self::CleanupThrow => (2, 3),
Self::CompareOp { .. } => (1, 2),
Self::CompareOpFloat => (1, 2),
Self::CompareOpInt => (1, 2),
Self::CompareOpStr => (1, 2),
Self::ContainsOp(_) => (1, 2),
Self::ContainsOpDict => (1, 2),
Self::ContainsOpSet => (1, 2),
Self::ConvertValue { .. } => (1, 1),
Self::Copy { .. } => (2 + (oparg - 1), 1 + (oparg - 1)),
Self::CopyFreeVars { .. } => (0, 0),
Self::DeleteAttr { .. } => (0, 1),
Self::DeleteDeref(_) => (0, 0),
Self::DeleteFast(_) => (0, 0),
Self::DeleteGlobal(_) => (0, 0),
Self::DeleteName(_) => (0, 0),
Self::DeleteSubscr => (0, 2),
Self::DictMerge { .. } => (4 + (oparg - 1), 5 + (oparg - 1)),
Self::DictUpdate { .. } => (1 + (oparg - 1), 2 + (oparg - 1)),
Self::EndAsyncFor => (0, 2),
Self::EndFor => (0, 1),
Self::EndSend => (1, 2),
Self::EnterExecutor => (0, 0),
Self::ExitInitCheck => (0, 1),
Self::ExtendedArg => (0, 0),
Self::ForIter { .. } => (2, 1),
Self::ForIterGen => (1, 1),
Self::ForIterList => (2, 1),
Self::ForIterRange => (2, 1),
Self::ForIterTuple => (2, 1),
Self::FormatSimple => (1, 1),
Self::FormatWithSpec => (1, 2),
Self::GetAIter => (1, 1),
Self::GetANext => (2, 1),
Self::GetAwaitable { .. } => (1, 1),
Self::GetIter => (1, 1),
Self::GetLen => (2, 1),
Self::GetYieldFromIter => (1, 1),
Self::ImportFrom { .. } => (2, 1),
Self::ImportName { .. } => (1, 2),
Self::InstrumentedCall => (1, 2 + oparg),
Self::InstrumentedCallFunctionEx => (1, 4),
Self::InstrumentedCallKw => (1, 3 + oparg),
Self::InstrumentedEndAsyncFor => (0, 2),
Self::InstrumentedEndFor => (1, 2),
Self::InstrumentedEndSend => (1, 2),
Self::InstrumentedForIter => (2, 1),
Self::InstrumentedInstruction => (0, 0),
Self::InstrumentedJumpBackward => (0, 0),
Self::InstrumentedJumpForward => (0, 0),
Self::InstrumentedLine => (0, 0),
Self::InstrumentedLoadSuperAttr => (1 + (oparg & 1), 3),
Self::InstrumentedNotTaken => (0, 0),
Self::InstrumentedPopIter => (0, 1),
Self::InstrumentedPopJumpIfFalse => (0, 1),
Self::InstrumentedPopJumpIfNone => (0, 1),
Self::InstrumentedPopJumpIfNotNone => (0, 1),
Self::InstrumentedPopJumpIfTrue => (0, 1),
Self::InstrumentedResume => (0, 0),
Self::InstrumentedReturnValue => (1, 1),
Self::InstrumentedYieldValue => (1, 1),
Self::InterpreterExit => (0, 1),
Self::IsOp(_) => (1, 2),
Self::JumpBackward { .. } => (0, 0),
Self::JumpBackwardJit => (0, 0),
Self::JumpBackwardNoInterrupt { .. } => (0, 0),
Self::JumpBackwardNoJit => (0, 0),
Self::JumpForward { .. } => (0, 0),
Self::ListAppend { .. } => (1 + (oparg - 1), 2 + (oparg - 1)),
Self::ListExtend { .. } => (1 + (oparg - 1), 2 + (oparg - 1)),
Self::LoadAttr { .. } => (1 + (oparg & 1), 1),
Self::LoadAttrClass => (1 + (oparg & 1), 1),
Self::LoadAttrClassWithMetaclassCheck => (1 + (oparg & 1), 1),
Self::LoadAttrGetattributeOverridden => (1, 1),
Self::LoadAttrInstanceValue => (1 + (oparg & 1), 1),
Self::LoadAttrMethodLazyDict => (2, 1),
Self::LoadAttrMethodNoDict => (2, 1),
Self::LoadAttrMethodWithValues => (2, 1),
Self::LoadAttrModule => (1 + (oparg & 1), 1),
Self::LoadAttrNondescriptorNoDict => (1, 1),
Self::LoadAttrNondescriptorWithValues => (1, 1),
Self::LoadAttrProperty => (0, 1),
Self::LoadAttrSlot => (1 + (oparg & 1), 1),
Self::LoadAttrWithHint => (1 + (oparg & 1), 1),
Self::LoadBuildClass => (1, 0),
Self::LoadCommonConstant { .. } => (1, 0),
Self::LoadConst { .. } => (1, 0),
Self::LoadConstImmortal => (1, 0),
Self::LoadConstMortal => (1, 0),
Self::LoadDeref(_) => (1, 0),
Self::LoadFast(_) => (1, 0),
Self::LoadFastAndClear(_) => (1, 0),
Self::LoadFastBorrow(_) => (1, 0),
Self::LoadFastBorrowLoadFastBorrow { .. } => (2, 0),
Self::LoadFastCheck(_) => (1, 0),
Self::LoadFastLoadFast { .. } => (2, 0),
Self::LoadFromDictOrDeref(_) => (1, 1),
Self::LoadFromDictOrGlobals(_) => (1, 1),
Self::LoadGlobal(_) => (
1, // TODO: Differs from CPython `1 + (oparg & 1)`
0,
),
Self::LoadGlobalBuiltin => (1 + (oparg & 1), 0),
Self::LoadGlobalModule => (1 + (oparg & 1), 0),
Self::LoadLocals => (1, 0),
Self::LoadName(_) => (1, 0),
Self::LoadSmallInt { .. } => (1, 0),
Self::LoadSpecial { .. } => (1, 1),
Self::LoadSuperAttr { .. } => (1 + (oparg & 1), 3),
Self::LoadSuperAttrAttr => (1, 3),
Self::LoadSuperAttrMethod => (2, 3),
Self::MakeCell(_) => (0, 0),
Self::MakeFunction { .. } => (1, 1),
Self::MapAdd { .. } => (1 + (oparg - 1), 3 + (oparg - 1)),
Self::MatchClass { .. } => (1, 3),
Self::MatchKeys { .. } => (3, 2),
Self::MatchMapping => (2, 1),
Self::MatchSequence => (2, 1),
Self::Nop => (0, 0),
Self::NotTaken => (0, 0),
Self::PopExcept => (0, 1),
Self::PopIter => (0, 1),
Self::PopJumpIfFalse { .. } => (0, 1),
Self::PopJumpIfNone { .. } => (0, 1),
Self::PopJumpIfNotNone { .. } => (0, 1),
Self::PopJumpIfTrue { .. } => (0, 1),
Self::PopTop => (0, 1),
Self::PushExcInfo => (2, 1),
Self::PushNull => (1, 0),
Self::RaiseVarargs { kind } => (
0,
// TODO: Differs from CPython: `oparg`
match kind.get((oparg as u32).into()) {
RaiseKind::BareRaise => 0,
RaiseKind::Raise => 1,
RaiseKind::RaiseCause => 2,
RaiseKind::ReraiseFromStack => 1,
},
),
Self::Reraise { .. } => (
1 + oparg, // TODO: Differs from CPython: `oparg`
1 + oparg,
),
Self::Reserved => (0, 0),
Self::Resume { .. } => (0, 0),
Self::ResumeCheck => (0, 0),
Self::ReturnGenerator => (1, 0),
Self::ReturnValue => (
0, // TODO: Differs from CPython: `1`
1,
),
Self::Send { .. } => (2, 2),
Self::SendGen => (1, 2),
Self::SetAdd { .. } => (1 + (oparg - 1), 2 + (oparg - 1)),
Self::SetFunctionAttribute { .. } => (1, 2),
Self::SetUpdate { .. } => (1 + (oparg - 1), 2 + (oparg - 1)),
Self::SetupAnnotations => (0, 0),
Self::StoreAttr { .. } => (0, 2),
Self::StoreAttrInstanceValue => (0, 2),
Self::StoreAttrSlot => (0, 2),
Self::StoreAttrWithHint => (0, 2),
Self::StoreDeref(_) => (0, 1),
Self::StoreFast(_) => (0, 1),
Self::StoreFastLoadFast { .. } => (1, 1),
Self::StoreFastStoreFast { .. } => (0, 2),
Self::StoreGlobal(_) => (0, 1),
Self::StoreName(_) => (0, 1),
Self::StoreSlice => (0, 4),
Self::StoreSubscr => (0, 3),
Self::StoreSubscrDict => (0, 3),
Self::StoreSubscrListInt => (0, 3),
Self::Swap { .. } => (2 + (oparg - 2), 2 + (oparg - 2)),
Self::ToBool => (1, 1),
Self::ToBoolAlwaysTrue => (1, 1),
Self::ToBoolBool => (1, 1),
Self::ToBoolInt => (1, 1),
Self::ToBoolList => (1, 1),
Self::ToBoolNone => (1, 1),
Self::ToBoolStr => (1, 1),
Self::UnaryInvert => (1, 1),
Self::UnaryNegative => (1, 1),
Self::UnaryNot => (1, 1),
Self::UnpackEx { .. } => (1 + (oparg & 0xFF) + (oparg >> 8), 1),
Self::UnpackSequence { .. } => (oparg, 1),
Self::UnpackSequenceList => (oparg, 1),
Self::UnpackSequenceTuple => (oparg, 1),
Self::UnpackSequenceTwoTuple => (2, 1),
Self::WithExceptStart => (6, 5),
Self::YieldValue { .. } => (1, 1),
};
debug_assert!((0..=i32::MAX).contains(&pushed));
debug_assert!((0..=i32::MAX).contains(&popped));
StackEffect::new(pushed as u32, popped as u32)
}
#[allow(clippy::too_many_arguments)]
fn fmt_dis(
&self,
arg: OpArg,
f: &mut fmt::Formatter<'_>,
ctx: &impl InstrDisplayContext,
expand_code_objects: bool,
pad: usize,
level: usize,
) -> fmt::Result {
macro_rules! w {
($variant:ident) => {
write!(f, stringify!($variant))
};
($variant:ident, $map:ident = $arg_marker:expr) => {{
let arg = $arg_marker.get(arg);
write!(f, "{:pad$}({}, {})", stringify!($variant), arg, $map(arg))
}};
($variant:ident, $arg_marker:expr) => {
write!(f, "{:pad$}({})", stringify!($variant), $arg_marker.get(arg))
};
($variant:ident, ?$arg_marker:expr) => {
write!(
f,
"{:pad$}({:?})",
stringify!($variant),
$arg_marker.get(arg)
)
};
}
let varname = |i: u32| ctx.get_varname(i as usize);
let name = |i: u32| ctx.get_name(i as usize);
let cell_name = |i: u32| ctx.get_cell_name(i as usize);
let fmt_const =
|op: &str, arg: OpArg, f: &mut fmt::Formatter<'_>, idx: &Arg<u32>| -> fmt::Result {
let value = ctx.get_constant(idx.get(arg) as usize);
match value.borrow_constant() {
BorrowedConstant::Code { code } if expand_code_objects => {
write!(f, "{op:pad$}({code:?}):")?;
code.display_inner(f, true, level + 1)?;
Ok(())
}
c => {
write!(f, "{op:pad$}(")?;
c.fmt_display(f)?;
write!(f, ")")
}
}
};
match self {
Self::BinaryOp { op } => write!(f, "{:pad$}({})", "BINARY_OP", op.get(arg)),
Self::BuildList { size } => w!(BUILD_LIST, size),
Self::BuildMap { size } => w!(BUILD_MAP, size),
Self::BuildSet { size } => w!(BUILD_SET, size),
Self::BuildSlice { argc } => w!(BUILD_SLICE, ?argc),
Self::BuildString { size } => w!(BUILD_STRING, size),
Self::BuildTuple { size } => w!(BUILD_TUPLE, size),
Self::Call { nargs } => w!(CALL, nargs),
Self::CallFunctionEx => w!(CALL_FUNCTION_EX),
Self::CallKw { nargs } => w!(CALL_KW, nargs),
Self::CallIntrinsic1 { func } => w!(CALL_INTRINSIC_1, ?func),
Self::CallIntrinsic2 { func } => w!(CALL_INTRINSIC_2, ?func),
Self::CheckEgMatch => w!(CHECK_EG_MATCH),
Self::CheckExcMatch => w!(CHECK_EXC_MATCH),
Self::CleanupThrow => w!(CLEANUP_THROW),
Self::CompareOp { op } => w!(COMPARE_OP, ?op),
Self::ContainsOp(inv) => w!(CONTAINS_OP, ?inv),
Self::ConvertValue { oparg } => write!(f, "{:pad$}{}", "CONVERT_VALUE", oparg.get(arg)),
Self::Copy { index } => w!(COPY, index),
Self::DeleteAttr { idx } => w!(DELETE_ATTR, name = idx),
Self::DeleteDeref(idx) => w!(DELETE_DEREF, cell_name = idx),
Self::DeleteFast(idx) => w!(DELETE_FAST, varname = idx),
Self::DeleteGlobal(idx) => w!(DELETE_GLOBAL, name = idx),
Self::DeleteName(idx) => w!(DELETE_NAME, name = idx),
Self::DeleteSubscr => w!(DELETE_SUBSCR),
Self::DictMerge { index } => w!(DICT_MERGE, index),
Self::DictUpdate { index } => w!(DICT_UPDATE, index),
Self::EndAsyncFor => w!(END_ASYNC_FOR),
Self::EndSend => w!(END_SEND),
Self::ExtendedArg => w!(EXTENDED_ARG, Arg::<u32>::marker()),
Self::ForIter { target } => w!(FOR_ITER, target),
Self::FormatSimple => w!(FORMAT_SIMPLE),
Self::FormatWithSpec => w!(FORMAT_WITH_SPEC),
Self::GetAIter => w!(GET_AITER),
Self::GetANext => w!(GET_ANEXT),
Self::GetAwaitable { arg } => w!(GET_AWAITABLE, arg),
Self::Reserved => w!(RESERVED),
Self::GetIter => w!(GET_ITER),
Self::GetLen => w!(GET_LEN),
Self::ImportFrom { idx } => w!(IMPORT_FROM, name = idx),
Self::ImportName { idx } => w!(IMPORT_NAME, name = idx),
Self::IsOp(inv) => w!(IS_OP, ?inv),
Self::JumpBackward { target } => w!(JUMP_BACKWARD, target),
Self::JumpBackwardNoInterrupt { target } => w!(JUMP_BACKWARD_NO_INTERRUPT, target),
Self::JumpForward { target } => w!(JUMP_FORWARD, target),
Self::ListAppend { i } => w!(LIST_APPEND, i),
Self::ListExtend { i } => w!(LIST_EXTEND, i),
Self::LoadAttr { idx } => {
let oparg = idx.get(arg);
let oparg_u32 = u32::from(oparg);
let attr_name = name(oparg.name_idx());
if oparg.is_method() {
write!(
f,
"{:pad$}({}, {}, method=true)",
"LOAD_ATTR", oparg_u32, attr_name
)
} else {
write!(f, "{:pad$}({}, {})", "LOAD_ATTR", oparg_u32, attr_name)
}
}
Self::LoadBuildClass => w!(LOAD_BUILD_CLASS),
Self::LoadFromDictOrDeref(i) => w!(LOAD_FROM_DICT_OR_DEREF, cell_name = i),
Self::LoadConst { idx } => fmt_const("LOAD_CONST", arg, f, idx),
Self::LoadSmallInt { idx } => w!(LOAD_SMALL_INT, idx),
Self::LoadDeref(idx) => w!(LOAD_DEREF, cell_name = idx),
Self::LoadFast(idx) => w!(LOAD_FAST, varname = idx),
Self::LoadFastAndClear(idx) => w!(LOAD_FAST_AND_CLEAR, varname = idx),
Self::LoadFastBorrow(idx) => w!(LOAD_FAST_BORROW, varname = idx),
Self::LoadFastCheck(idx) => w!(LOAD_FAST_CHECK, varname = idx),
Self::LoadFastLoadFast { arg: packed } => {
let oparg = packed.get(arg);
let idx1 = oparg >> 4;
let idx2 = oparg & 15;
let name1 = varname(idx1);
let name2 = varname(idx2);
write!(f, "{:pad$}({}, {})", "LOAD_FAST_LOAD_FAST", name1, name2)
}
Self::LoadFastBorrowLoadFastBorrow { arg: packed } => {
let oparg = packed.get(arg);
let idx1 = oparg >> 4;
let idx2 = oparg & 15;
let name1 = varname(idx1);
let name2 = varname(idx2);
write!(
f,
"{:pad$}({}, {})",
"LOAD_FAST_BORROW_LOAD_FAST_BORROW", name1, name2
)
}
Self::LoadFromDictOrGlobals(idx) => w!(LOAD_FROM_DICT_OR_GLOBALS, name = idx),
Self::LoadGlobal(idx) => w!(LOAD_GLOBAL, name = idx),
Self::LoadName(idx) => w!(LOAD_NAME, name = idx),
Self::LoadSpecial { method } => w!(LOAD_SPECIAL, method),
Self::LoadSuperAttr { arg: idx } => {
let oparg = idx.get(arg);
write!(
f,
"{:pad$}({}, {}, method={}, class={})",
"LOAD_SUPER_ATTR",
u32::from(oparg),
name(oparg.name_idx()),
oparg.is_load_method(),
oparg.has_class()
)
}
Self::MakeFunction => w!(MAKE_FUNCTION),
Self::MapAdd { i } => w!(MAP_ADD, i),
Self::MatchClass(arg) => w!(MATCH_CLASS, arg),
Self::MatchKeys => w!(MATCH_KEYS),
Self::MatchMapping => w!(MATCH_MAPPING),
Self::MatchSequence => w!(MATCH_SEQUENCE),
Self::Nop => w!(NOP),
Self::PopExcept => w!(POP_EXCEPT),
Self::PopJumpIfFalse { target } => w!(POP_JUMP_IF_FALSE, target),
Self::PopJumpIfTrue { target } => w!(POP_JUMP_IF_TRUE, target),
Self::PopTop => w!(POP_TOP),
Self::EndFor => w!(END_FOR),
Self::PopIter => w!(POP_ITER),
Self::PushExcInfo => w!(PUSH_EXC_INFO),
Self::PushNull => w!(PUSH_NULL),
Self::RaiseVarargs { kind } => w!(RAISE_VARARGS, ?kind),
Self::Reraise { depth } => w!(RERAISE, depth),
Self::Resume { arg } => w!(RESUME, arg),
Self::ReturnValue => w!(RETURN_VALUE),
Self::ReturnGenerator => w!(RETURN_GENERATOR),
Self::Send { target } => w!(SEND, target),
Self::SetAdd { i } => w!(SET_ADD, i),
Self::SetFunctionAttribute { attr } => w!(SET_FUNCTION_ATTRIBUTE, ?attr),
Self::SetupAnnotations => w!(SETUP_ANNOTATIONS),
Self::SetUpdate { i } => w!(SET_UPDATE, i),
Self::StoreAttr { idx } => w!(STORE_ATTR, name = idx),
Self::StoreDeref(idx) => w!(STORE_DEREF, cell_name = idx),
Self::StoreFast(idx) => w!(STORE_FAST, varname = idx),
Self::StoreFastLoadFast {
store_idx,
load_idx,
} => {
write!(f, "STORE_FAST_LOAD_FAST")?;
write!(f, " ({}, {})", store_idx.get(arg), load_idx.get(arg))
}
Self::StoreGlobal(idx) => w!(STORE_GLOBAL, name = idx),
Self::StoreName(idx) => w!(STORE_NAME, name = idx),
Self::StoreSubscr => w!(STORE_SUBSCR),
Self::Swap { index } => w!(SWAP, index),
Self::ToBool => w!(TO_BOOL),
Self::UnpackEx { args } => w!(UNPACK_EX, args),
Self::UnpackSequence { size } => w!(UNPACK_SEQUENCE, size),
Self::WithExceptStart => w!(WITH_EXCEPT_START),
Self::UnaryInvert => w!(UNARY_INVERT),
Self::UnaryNegative => w!(UNARY_NEGATIVE),
Self::UnaryNot => w!(UNARY_NOT),
Self::YieldValue { arg } => w!(YIELD_VALUE, arg),
Self::GetYieldFromIter => w!(GET_YIELD_FROM_ITER),
Self::BuildTemplate => w!(BUILD_TEMPLATE),
Self::BuildInterpolation { oparg } => w!(BUILD_INTERPOLATION, oparg),
_ => w!(RUSTPYTHON_PLACEHOLDER),
}
}
}
/// Instructions used by the compiler. They are not executed by the VM.
///
/// CPython 3.14.2 aligned (256-266).
#[derive(Clone, Copy, Debug)]
#[repr(u16)]
pub enum PseudoInstruction {
// CPython 3.14.2 pseudo instructions (256-266)
AnnotationsPlaceholder = 256,
Jump { target: Arg<Label> } = 257,
JumpIfFalse { target: Arg<Label> } = 258,
JumpIfTrue { target: Arg<Label> } = 259,
JumpNoInterrupt { target: Arg<Label> } = 260,
LoadClosure(Arg<NameIdx>) = 261,
PopBlock = 262,
SetupCleanup { target: Arg<Label> } = 263,
SetupFinally { target: Arg<Label> } = 264,
SetupWith { target: Arg<Label> } = 265,
StoreFastMaybeNull(Arg<NameIdx>) = 266,
}
const _: () = assert!(mem::size_of::<PseudoInstruction>() == 2);
impl From<PseudoInstruction> for u16 {
#[inline]
fn from(ins: PseudoInstruction) -> Self {
// SAFETY: there's no padding bits
unsafe { mem::transmute::<PseudoInstruction, Self>(ins) }
}
}
impl TryFrom<u16> for PseudoInstruction {
type Error = MarshalError;
#[inline]
fn try_from(value: u16) -> Result<Self, MarshalError> {
let start = u16::from(Self::AnnotationsPlaceholder);
let end = u16::from(Self::StoreFastMaybeNull(Arg::marker()));
if (start..=end).contains(&value) {
Ok(unsafe { mem::transmute::<u16, Self>(value) })
} else {
Err(Self::Error::InvalidBytecode)
}
}
}
impl PseudoInstruction {
/// Returns true if this is a block push pseudo instruction
/// (SETUP_FINALLY, SETUP_CLEANUP, or SETUP_WITH).
pub fn is_block_push(&self) -> bool {
matches!(
self,
Self::SetupCleanup { .. } | Self::SetupFinally { .. } | Self::SetupWith { .. }
)
}
}
impl InstructionMetadata for PseudoInstruction {
fn label_arg(&self) -> Option<Arg<Label>> {
match self {
Self::Jump { target: l }
| Self::JumpIfFalse { target: l }
| Self::JumpIfTrue { target: l }
| Self::JumpNoInterrupt { target: l }
| Self::SetupCleanup { target: l }
| Self::SetupFinally { target: l }
| Self::SetupWith { target: l } => Some(*l),
_ => None,
}
}
fn is_scope_exit(&self) -> bool {
false
}
fn stack_effect_info(&self, _oparg: u32) -> StackEffect {
// Reason for converting oparg to i32 is because of expressions like `1 + (oparg -1)`
// that causes underflow errors.
let _oparg = i32::try_from(_oparg).expect("oparg does not fit in an `i32`");
// NOTE: Please don't "simplify" expressions here (i.e. `1 + (oparg - 1)`)
// as it will be harder to see diff with what CPython auto-generates
let (pushed, popped) = match self {
Self::AnnotationsPlaceholder => (0, 0),
Self::Jump { .. } => (0, 0),
Self::JumpIfFalse { .. } => (1, 1),
Self::JumpIfTrue { .. } => (1, 1),
Self::JumpNoInterrupt { .. } => (0, 0),
Self::LoadClosure(_) => (1, 0),
Self::PopBlock => (0, 0),
// Normal path effect is 0 (these are NOPs on fall-through).
// Handler entry effects are computed directly in max_stackdepth().
Self::SetupCleanup { .. } => (0, 0),
Self::SetupFinally { .. } => (0, 0),
Self::SetupWith { .. } => (0, 0),
Self::StoreFastMaybeNull(_) => (0, 1),
};
debug_assert!((0..=i32::MAX).contains(&pushed));
debug_assert!((0..=i32::MAX).contains(&popped));
StackEffect::new(pushed as u32, popped as u32)
}
fn is_unconditional_jump(&self) -> bool {
matches!(self, Self::Jump { .. } | Self::JumpNoInterrupt { .. })
}
fn fmt_dis(
&self,
_arg: OpArg,
_f: &mut fmt::Formatter<'_>,
_ctx: &impl InstrDisplayContext,
_expand_code_objects: bool,
_pad: usize,
_level: usize,
) -> fmt::Result {
unimplemented!()
}
}
#[derive(Clone, Copy, Debug)]
pub enum AnyInstruction {
Real(Instruction),
Pseudo(PseudoInstruction),
}
impl From<Instruction> for AnyInstruction {
fn from(value: Instruction) -> Self {
Self::Real(value)
}
}
impl From<PseudoInstruction> for AnyInstruction {
fn from(value: PseudoInstruction) -> Self {
Self::Pseudo(value)
}
}
impl TryFrom<u8> for AnyInstruction {
type Error = MarshalError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(Instruction::try_from(value)?.into())
}
}
impl TryFrom<u16> for AnyInstruction {
type Error = MarshalError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match u8::try_from(value) {
Ok(v) => v.try_into(),
Err(_) => Ok(PseudoInstruction::try_from(value)?.into()),
}
}
}
macro_rules! inst_either {
(fn $name:ident ( &self $(, $arg:ident : $arg_ty:ty )* ) -> $ret:ty ) => {
fn $name(&self $(, $arg : $arg_ty )* ) -> $ret {
match self {
Self::Real(op) => op.$name($($arg),*),
Self::Pseudo(op) => op.$name($($arg),*),
}
}
};
}
impl InstructionMetadata for AnyInstruction {
inst_either!(fn label_arg(&self) -> Option<Arg<Label>>);
inst_either!(fn is_unconditional_jump(&self) -> bool);
inst_either!(fn is_scope_exit(&self) -> bool);
inst_either!(fn stack_effect(&self, oparg: u32) -> i32);
inst_either!(fn stack_effect_info(&self, oparg: u32) -> StackEffect);
inst_either!(fn fmt_dis(
&self,
arg: OpArg,
f: &mut fmt::Formatter<'_>,
ctx: &impl InstrDisplayContext,
expand_code_objects: bool,
pad: usize,
level: usize
) -> fmt::Result);
}
impl AnyInstruction {
/// Gets the inner value of [`Self::Real`].
pub const fn real(self) -> Option<Instruction> {
match self {
Self::Real(ins) => Some(ins),
_ => None,
}
}
/// Gets the inner value of [`Self::Pseudo`].
pub const fn pseudo(self) -> Option<PseudoInstruction> {
match self {
Self::Pseudo(ins) => Some(ins),
_ => None,
}
}
/// Same as [`Self::real`] but panics if wasn't called on [`Self::Real`].
///
/// # Panics
///
/// If was called on something else other than [`Self::Real`].
pub const fn expect_real(self) -> Instruction {
self.real()
.expect("Expected Instruction::Real, found Instruction::Pseudo")
}
/// Same as [`Self::pseudo`] but panics if wasn't called on [`Self::Pseudo`].
///
/// # Panics
///
/// If was called on something else other than [`Self::Pseudo`].
pub const fn expect_pseudo(self) -> PseudoInstruction {
self.pseudo()
.expect("Expected Instruction::Pseudo, found Instruction::Real")
}
/// Returns true if this is a block push pseudo instruction
/// (SETUP_FINALLY, SETUP_CLEANUP, or SETUP_WITH).
pub fn is_block_push(&self) -> bool {
matches!(self, Self::Pseudo(p) if p.is_block_push())
}
/// Returns true if this is a POP_BLOCK pseudo instruction.
pub fn is_pop_block(&self) -> bool {
matches!(self, Self::Pseudo(PseudoInstruction::PopBlock))
}
}
/// What effect the instruction has on the stack.
#[derive(Clone, Copy)]
pub struct StackEffect {
/// How many items the instruction is pushing on the stack.
pushed: u32,
/// How many items the instruction is popping from the stack.
popped: u32,
}
impl StackEffect {
/// Creates a new [`Self`].
pub const fn new(pushed: u32, popped: u32) -> Self {
Self { pushed, popped }
}
/// Get the calculated stack effect as [`i32`].
pub fn effect(self) -> i32 {
self.into()
}
/// Get the pushed count.
pub const fn pushed(self) -> u32 {
self.pushed
}
/// Get the popped count.
pub const fn popped(self) -> u32 {
self.popped
}
}
impl From<StackEffect> for i32 {
fn from(effect: StackEffect) -> Self {
(effect.pushed() as i32) - (effect.popped() as i32)
}
}
pub trait InstructionMetadata {
/// Gets the label stored inside this instruction, if it exists.
fn label_arg(&self) -> Option<Arg<Label>>;
fn is_scope_exit(&self) -> bool;
fn is_unconditional_jump(&self) -> bool;
/// Stack effect info for how many items are pushed/popped from the stack,
/// for this instruction.
fn stack_effect_info(&self, oparg: u32) -> StackEffect;
/// Stack effect of [`Self::stack_effect_info`].
fn stack_effect(&self, oparg: u32) -> i32 {
self.stack_effect_info(oparg).effect()
}
#[allow(clippy::too_many_arguments)]
fn fmt_dis(
&self,
arg: OpArg,
f: &mut fmt::Formatter<'_>,
ctx: &impl InstrDisplayContext,
expand_code_objects: bool,
pad: usize,
level: usize,
) -> fmt::Result;
fn display(&self, arg: OpArg, ctx: &impl InstrDisplayContext) -> impl fmt::Display {
fmt::from_fn(move |f| self.fmt_dis(arg, f, ctx, false, 0, 0))
}
}
#[derive(Copy, Clone)]
pub struct Arg<T: OpArgType>(PhantomData<T>);
impl<T: OpArgType> Arg<T> {
#[inline]
pub const fn marker() -> Self {
Self(PhantomData)
}
#[inline]
pub fn new(arg: T) -> (Self, OpArg) {
(Self(PhantomData), OpArg::new(arg.into()))
}
#[inline]
pub fn new_single(arg: T) -> (Self, OpArgByte)
where
T: Into<u8>,
{
(Self(PhantomData), OpArgByte::new(arg.into()))
}
#[inline(always)]
pub fn get(self, arg: OpArg) -> T {
self.try_get(arg).unwrap()
}
#[inline(always)]
pub fn try_get(self, arg: OpArg) -> Result<T, MarshalError> {
T::try_from(u32::from(arg)).map_err(|_| MarshalError::InvalidBytecode)
}
/// # Safety
/// T::from_op_arg(self) must succeed
#[inline(always)]
pub unsafe fn get_unchecked(self, arg: OpArg) -> T {
// SAFETY: requirements forwarded from caller
unsafe { T::try_from(u32::from(arg)).unwrap_unchecked() }
}
}
impl<T: OpArgType> PartialEq for Arg<T> {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl<T: OpArgType> Eq for Arg<T> {}
impl<T: OpArgType> fmt::Debug for Arg<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Arg<{}>", core::any::type_name::<T>())
}
}