Skip to main content

rustpython_compiler_core/bytecode/
oparg.rs

1use core::fmt;
2
3use crate::{
4    bytecode::{CodeUnit, Instruction},
5    marshal::MarshalError,
6};
7
8pub trait OpArgType: Copy + Into<u32> + TryFrom<u32> {}
9
10/// Opcode argument that may be extended by a prior ExtendedArg.
11#[derive(Copy, Clone, PartialEq, Eq)]
12#[repr(transparent)]
13pub struct OpArgByte(u8);
14
15impl OpArgByte {
16    pub const NULL: Self = Self::new(0);
17
18    #[must_use]
19    pub const fn new(value: u8) -> Self {
20        Self(value)
21    }
22
23    /// Returns the inner value as a [`u8`].
24    #[must_use]
25    pub const fn as_u8(self) -> u8 {
26        self.0
27    }
28
29    /// Returns the inner value as a [`u32`].
30    #[must_use]
31    pub const fn as_u32(self) -> u32 {
32        self.0 as u32
33    }
34}
35
36impl From<u8> for OpArgByte {
37    fn from(raw: u8) -> Self {
38        Self::new(raw)
39    }
40}
41
42impl From<OpArgByte> for u8 {
43    fn from(value: OpArgByte) -> Self {
44        value.as_u8()
45    }
46}
47
48impl fmt::Debug for OpArgByte {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        self.0.fmt(f)
51    }
52}
53
54/// Full 32-bit op_arg, including any possible ExtendedArg extension.
55#[derive(Copy, Clone, Debug)]
56#[repr(transparent)]
57pub struct OpArg(u32);
58
59impl OpArg {
60    pub const NULL: Self = Self::new(0);
61
62    #[must_use]
63    pub const fn new(value: u32) -> Self {
64        Self(value)
65    }
66
67    /// Returns how many CodeUnits a instruction with this op_arg will be encoded as
68    #[inline]
69    #[must_use]
70    pub const fn instr_size(self) -> usize {
71        (self.0 > 0xff) as usize + (self.0 > 0xff_ff) as usize + (self.0 > 0xff_ff_ff) as usize + 1
72    }
73
74    /// returns the arg split into any necessary ExtendedArg components (in big-endian order) and
75    /// the arg for the real opcode itself
76    #[inline(always)]
77    pub fn split(self) -> (impl ExactSizeIterator<Item = OpArgByte>, OpArgByte) {
78        let mut it = self
79            .0
80            .to_le_bytes()
81            .map(OpArgByte)
82            .into_iter()
83            .take(self.instr_size());
84        let lo = it.next().unwrap();
85        (it.rev(), lo)
86    }
87}
88
89impl From<u32> for OpArg {
90    fn from(raw: u32) -> Self {
91        Self::new(raw)
92    }
93}
94
95impl From<OpArg> for u32 {
96    fn from(value: OpArg) -> Self {
97        value.0
98    }
99}
100
101#[derive(Default, Copy, Clone)]
102#[repr(transparent)]
103pub struct OpArgState {
104    state: u32,
105}
106
107impl OpArgState {
108    #[inline(always)]
109    pub const fn get(&mut self, ins: CodeUnit) -> (Instruction, OpArg) {
110        let arg = self.extend(ins.arg);
111        if !matches!(ins.op, Instruction::ExtendedArg) {
112            self.reset();
113        }
114        (ins.op, arg)
115    }
116
117    #[inline(always)]
118    pub const fn extend(&mut self, arg: OpArgByte) -> OpArg {
119        self.state = (self.state << 8) | arg.as_u32();
120        OpArg::new(self.state)
121    }
122
123    #[inline(always)]
124    pub const fn reset(&mut self) {
125        self.state = 0
126    }
127}
128
129/// Defines an enum whose variants map to fixed `u8` discriminants,
130/// and automatically implements the following traits:
131///
132/// - [`Display`](std::fmt::Display)
133/// - [`From`]`<EnumName> for u8` / `u32`
134/// - [`TryFrom`]`<u8> / <u32> for EnumName`
135/// - [`OpArgType`]
136///
137/// Along with the inherent methods `as_u8`, `as_u32`, `try_from_u8`, and `try_from_u32`.
138///
139/// # Variant syntax
140///
141/// Each variant is assigned a value using one of two forms:
142///
143/// | Form | Syntax | `Display` output |
144/// |---|---|---|
145/// | Numeric | `Variant = 0` | `0` |
146/// | Labeled | `Variant = (0, "label")` | `label` |
147///
148/// # Example
149///
150/// ```ignore
151/// oparg_enum! {
152///     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
153///     pub enum MyArg {
154///         /// No argument.
155///         None  = 0,
156///         /// A small argument, displayed as "small".
157///         Small = (1, "small"),
158///         /// A large argument, displayed as "large".
159///         Large = (2, "large"),
160///     }
161/// }
162///
163/// assert_eq!(MyArg::None.as_u8(), 0);
164/// assert_eq!(MyArg::Small.as_u8(), 1);
165///
166/// assert_eq!(MyArg::try_from_u8(2), Ok(MyArg::Large));
167/// assert_eq!(u8::from(MyArg::None), 0u8);
168///
169/// assert_eq!(MyArg::None.to_string(),  "0");
170/// assert_eq!(MyArg::Small.to_string(), "small");
171/// assert_eq!(MyArg::Large.to_string(), "large");
172///
173/// // Format specs are respected
174/// assert_eq!(format!("{:>10}", MyArg::Small), "     small");
175/// ```
176macro_rules! oparg_enum {
177    (
178        $(#[$enum_meta:meta])*
179        $vis:vis enum $name:ident {
180            $(
181                $(#[$variant_meta:meta])*
182                $variant:ident = $value:tt
183            ),* $(,)?
184        }
185    ) => {
186        $(#[$enum_meta])*
187        $vis enum $name {
188            $(
189                $(#[$variant_meta])*
190                $variant, // Do assign value to variant.
191            )*
192        }
193
194        impl_oparg_enum!(
195            $vis enum $name {
196                $(
197                    $variant = $value,
198                )*
199            }
200        );
201    };
202}
203
204macro_rules! impl_oparg_enum {
205    (
206        $vis:vis enum $name:ident {
207            $(
208                $variant:ident = $value:tt
209            ),* $(,)?
210        }
211    ) => {
212        impl $name {
213            /// Returns the oparg as a [`u8`] value.
214            #[must_use]
215            $vis const fn as_u8(self) -> u8 {
216                match self {
217                    $(
218                        Self::$variant => impl_oparg_enum!(@discriminant $value),
219                    )*
220                }
221            }
222
223            /// Returns the oparg as a [`u32`] value.
224            #[must_use]
225            $vis const fn as_u32(self) -> u32 {
226                self.as_u8() as u32
227            }
228
229            $vis const fn try_from_u8(value: u8) -> Result<Self, $crate::marshal::MarshalError> {
230                Ok(match value {
231                    $(
232                        impl_oparg_enum!(@discriminant $value) => Self::$variant,
233                    )*
234                    _ => return Err($crate::marshal::MarshalError::InvalidBytecode),
235                })
236            }
237
238            $vis const fn try_from_u32(value: u32) -> Result<Self, $crate::marshal::MarshalError> {
239                if value > (u8::MAX as u32) {
240                    return Err($crate::marshal::MarshalError::InvalidBytecode);
241                }
242
243                // We already validated this is a lossles cast.
244                Self::try_from_u8(value as u8)
245            }
246
247            /// Iterate over the variants.
248            $vis fn iter() -> impl Iterator<Item = Self> {
249                [$(Self::$variant),*].iter().copied()
250            }
251        }
252
253        impl TryFrom<u8> for $name {
254            type Error = $crate::marshal::MarshalError;
255
256            fn try_from(value: u8) -> Result<Self, Self::Error> {
257                Self::try_from_u8(value)
258            }
259        }
260
261        impl TryFrom<u32> for $name {
262            type Error = $crate::marshal::MarshalError;
263
264            fn try_from(value: u32) -> Result<Self, Self::Error> {
265                Self::try_from_u32(value)
266            }
267        }
268
269        impl From<$name> for u8 {
270            fn from(value: $name) -> Self {
271                value.as_u8()
272            }
273        }
274
275        impl From<$name> for u32 {
276            fn from(value: $name) -> Self {
277                value.as_u32()
278            }
279        }
280
281        impl ::core::fmt::Display for $name {
282            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
283                match self {
284                    $(
285                        Self::$variant => impl_oparg_enum!(@display f, $value),
286                    )*
287                }
288            }
289        }
290
291        impl OpArgType for $name {}
292    };
293
294    (@discriminant ($num:literal, $str:literal)) => { $num };
295    (@discriminant $num:literal) => { $num };
296    (@display $f:expr, ($num:literal, $str:literal)) => {
297        ::core::fmt::Display::fmt($str, $f)
298    };
299    (@display $f:expr, $num:literal) => {
300        ::core::fmt::Display::fmt(&$num, $f)
301    };
302}
303
304oparg_enum!(
305    /// Oparg values for [`Instruction::ConvertValue`].
306    ///
307    /// ## See also
308    ///
309    /// - [CPython FVC_* flags](https://github.com/python/cpython/blob/v3.14.4/Include/ceval.h#L129-L132)
310    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
311    pub enum ConvertValueOparg {
312        /// No conversion.
313        ///
314        /// ```python
315        /// f"{x}"
316        /// f"{x:4}"
317        /// ```
318        // NOTE: We should never reach the display of this.
319        // `FVC_NONE` are being handled by `Instruction::FormatSimple`
320        None = 0,
321        /// Converts by calling `str(<value>)`.
322        ///
323        /// ```python
324        /// f"{x!s}"
325        /// f"{x!s:2}"
326        /// ```
327        Str = (1, "str"),
328        /// Converts by calling `repr(<value>)`.
329        ///
330        /// ```python
331        /// f"{x!r}"
332        /// f"{x!r:2}"
333        /// ```
334        Repr = (2, "repr"),
335        /// Converts by calling `ascii(<value>)`.
336        ///
337        /// ```python
338        /// f"{x!a}"
339        /// f"{x!a:2}"
340        /// ```
341        Ascii = (3, "ascii"),
342    }
343);
344
345pub type NameIdx = u32;
346
347impl OpArgType for u32 {}
348
349oparg_enum!(
350    /// The kind of Raise that occurred.
351    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
352    pub enum RaiseKind {
353        /// Bare `raise` statement with no arguments.
354        /// Gets the current exception from VM state (topmost_exception).
355        /// Maps to RAISE_VARARGS with oparg=0.
356        BareRaise = 0,
357        /// `raise exc` - exception is on the stack.
358        /// Maps to RAISE_VARARGS with oparg=1.
359        Raise = 1,
360        /// `raise exc from cause` - exception and cause are on the stack.
361        /// Maps to RAISE_VARARGS with oparg=2.
362        RaiseCause = 2,
363        /// Reraise exception from the stack top.
364        /// Used in exception handler cleanup blocks (finally, except).
365        /// Gets exception from stack, not from VM state.
366        /// Maps to the RERAISE opcode.
367        ReraiseFromStack = 3,
368    }
369);
370
371oparg_enum!(
372    /// Intrinsic function for CALL_INTRINSIC_1
373    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
374    pub enum IntrinsicFunction1 {
375        Invalid = 0,
376        Print = 1,
377        /// Import * operation
378        ImportStar = 2,
379        /// Convert StopIteration to RuntimeError in async context
380        StopIterationError = 3,
381        AsyncGenWrap = 4,
382        UnaryPositive = 5,
383        /// Convert list to tuple
384        ListToTuple = 6,
385        /// Type parameter related
386        TypeVar = 7,
387        ParamSpec = 8,
388        TypeVarTuple = 9,
389        /// Generic subscript for PEP 695
390        SubscriptGeneric = 10,
391        TypeAlias = 11,
392    }
393);
394
395impl IntrinsicFunction1 {
396    /// https://github.com/python/cpython/blob/v3.14.4/Include/internal/pycore_intrinsics.h#L9-L20
397    #[must_use]
398    pub const fn desc(&self) -> &str {
399        match self {
400            Self::Invalid => "INTRINSIC_1_INVALID",
401            Self::Print => "INTRINSIC_PRINT",
402            Self::ImportStar => "INTRINSIC_IMPORT_STAR",
403            Self::StopIterationError => "INTRINSIC_STOPITERATION_ERROR",
404            Self::AsyncGenWrap => "INTRINSIC_ASYNC_GEN_WRAP",
405            Self::UnaryPositive => "INTRINSIC_UNARY_POSITIVE",
406            Self::ListToTuple => "INTRINSIC_LIST_TO_TUPLE",
407            Self::TypeVar => "INTRINSIC_TYPEVAR",
408            Self::ParamSpec => "INTRINSIC_PARAMSPEC",
409            Self::TypeVarTuple => "INTRINSIC_TYPEVARTUPLE",
410            Self::SubscriptGeneric => "INTRINSIC_SUBSCRIPT_GENERIC",
411            Self::TypeAlias => "INTRINSIC_TYPEALIAS",
412        }
413    }
414}
415
416oparg_enum!(
417    /// Intrinsic function for CALL_INTRINSIC_2
418    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
419    pub enum IntrinsicFunction2 {
420        Invalid = 0,
421        PrepReraiseStar = 1,
422        TypeVarWithBound = 2,
423        TypeVarWithConstraint = 3,
424        SetFunctionTypeParams = 4,
425        /// Set default value for type parameter (PEP 695)
426        SetTypeparamDefault = 5,
427    }
428);
429
430impl IntrinsicFunction2 {
431    /// https://github.com/python/cpython/blob/v3.14.4/Include/internal/pycore_intrinsics.h#L26-L31
432    #[must_use]
433    pub const fn desc(&self) -> &str {
434        match self {
435            Self::Invalid => "INTRINSIC_2_INVALID",
436            Self::PrepReraiseStar => "INTRINSIC_PREP_RERAISE_STAR",
437            Self::TypeVarWithBound => "INTRINSIC_TYPEVAR_WITH_BOUND",
438            Self::TypeVarWithConstraint => "INTRINSIC_TYPEVAR_WITH_CONSTRAINTS",
439            Self::SetFunctionTypeParams => "INTRINSIC_SET_FUNCTION_TYPE_PARAMS",
440            Self::SetTypeparamDefault => "INTRINSIC_SET_TYPEPARAM_DEFAULT",
441        }
442    }
443}
444
445bitflagset::bitflag! {
446    /// `SET_FUNCTION_ATTRIBUTE` flags.
447    /// Bitmask: Defaults=0x01, KwOnly=0x02, Annotations=0x04,
448    /// Closure=0x08, TypeParams=0x10, Annotate=0x20.
449    /// Stored as bit position (0-5) by `bitflag!` macro.
450    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
451    #[repr(u8)]
452    pub enum MakeFunctionFlag {
453        Defaults = 0,
454        KwOnlyDefaults = 1,
455        Annotations = 2,
456        Closure = 3,
457        /// PEP 649: __annotate__ function closure (instead of __annotations__ dict)
458        Annotate = 4,
459        TypeParams = 5,
460    }
461}
462
463bitflagset::bitflagset! {
464    #[derive(Copy, Clone, PartialEq, Eq)]
465    pub struct MakeFunctionFlags(u8): MakeFunctionFlag
466}
467
468impl TryFrom<u32> for MakeFunctionFlag {
469    type Error = MarshalError;
470
471    /// Decode from CPython-compatible power-of-two value
472    fn try_from(value: u32) -> Result<Self, Self::Error> {
473        match value {
474            0x01 => Ok(Self::Defaults),
475            0x02 => Ok(Self::KwOnlyDefaults),
476            0x04 => Ok(Self::Annotations),
477            0x08 => Ok(Self::Closure),
478            0x10 => Ok(Self::Annotate),
479            0x20 => Ok(Self::TypeParams),
480            _ => Err(MarshalError::InvalidBytecode),
481        }
482    }
483}
484
485impl From<MakeFunctionFlag> for u32 {
486    /// Encode as CPython-compatible power-of-two value
487    fn from(flag: MakeFunctionFlag) -> Self {
488        1u32 << (flag as Self)
489    }
490}
491
492impl OpArgType for MakeFunctionFlag {}
493
494/// `COMPARE_OP` arg is `(cmp_index << 5) | mask`.
495///
496/// The low four bits are the CPython comparison mask used by specialized
497/// compare opcodes, and bit 4 requests bool-conversion of the compare result.
498pub const COMPARE_OP_BOOL_MASK: u32 = 1 << 4;
499
500#[derive(Debug, Copy, Clone, PartialEq, Eq)]
501pub enum ComparisonOperator {
502    Less,
503    LessOrEqual,
504    Equal,
505    NotEqual,
506    Greater,
507    GreaterOrEqual,
508}
509
510impl TryFrom<u8> for ComparisonOperator {
511    type Error = MarshalError;
512    fn try_from(value: u8) -> Result<Self, Self::Error> {
513        Self::try_from(value as u32)
514    }
515}
516
517impl TryFrom<u32> for ComparisonOperator {
518    type Error = MarshalError;
519    /// Decode from `COMPARE_OP` arg: `(cmp_index << 5) | mask`.
520    fn try_from(value: u32) -> Result<Self, Self::Error> {
521        match value >> 5 {
522            0 => Ok(Self::Less),
523            1 => Ok(Self::LessOrEqual),
524            2 => Ok(Self::Equal),
525            3 => Ok(Self::NotEqual),
526            4 => Ok(Self::Greater),
527            5 => Ok(Self::GreaterOrEqual),
528            _ => Err(MarshalError::InvalidBytecode),
529        }
530    }
531}
532
533impl From<ComparisonOperator> for u8 {
534    /// Encode using CPython's comparison mask layout.
535    fn from(value: ComparisonOperator) -> Self {
536        match value {
537            ComparisonOperator::Less => 2,
538            ComparisonOperator::LessOrEqual => (1 << 5) | 2 | 8,
539            ComparisonOperator::Equal => (2 << 5) | 8,
540            ComparisonOperator::NotEqual => (3 << 5) | 1 | 2 | 4,
541            ComparisonOperator::Greater => (4 << 5) | 4,
542            ComparisonOperator::GreaterOrEqual => (5 << 5) | 4 | 8,
543        }
544    }
545}
546
547impl From<ComparisonOperator> for u32 {
548    fn from(value: ComparisonOperator) -> Self {
549        Self::from(u8::from(value))
550    }
551}
552
553impl OpArgType for ComparisonOperator {}
554
555impl fmt::Display for ComparisonOperator {
556    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557        let op = match self {
558            Self::Less => "<",
559            Self::LessOrEqual => "<=",
560            Self::Equal => "==",
561            Self::NotEqual => "!=",
562            Self::Greater => ">",
563            Self::GreaterOrEqual => ">=",
564        };
565        f.write_str(op)
566    }
567}
568
569oparg_enum!(
570    /// The possible Binary operators
571    ///
572    /// # Examples
573    ///
574    /// ```rust
575    /// use rustpython_compiler_core::bytecode::{Arg, BinaryOperator, Instruction};
576    /// let (op, _) = Arg::new(BinaryOperator::Add);
577    /// let instruction = Instruction::BinaryOp { op };
578    /// ```
579    ///
580    /// See also:
581    /// - [_PyEval_BinaryOps](https://github.com/python/cpython/blob/8183fa5e3f78ca6ab862de7fb8b14f3d929421e0/Python/ceval.c#L316-L343)
582    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
583    pub enum BinaryOperator {
584        /// `+`
585        Add = (0, "+"),
586        /// `&`
587        And = (1, "&"),
588        /// `//`
589        FloorDivide = (2, "//"),
590        /// `<<`
591        Lshift = (3, "<<"),
592        /// `@`
593        MatrixMultiply = (4, "@"),
594        /// `*`
595        Multiply = (5, "*"),
596        /// `%`
597        Remainder = (6, "%"),
598        /// `|`
599        Or = (7, "|"),
600        /// `**`
601        Power = (8, "**"),
602        /// `>>`
603        Rshift = (9, ">>"),
604        /// `-`
605        Subtract = (10, "-"),
606        /// `/`
607        TrueDivide = (11, "/"),
608        /// `^`
609        Xor = (12, "^"),
610        /// `+=`
611        InplaceAdd = (13, "+="),
612        /// `&=`
613        InplaceAnd = (14, "&="),
614        /// `//=`
615        InplaceFloorDivide = (15, "//="),
616        /// `<<=`
617        InplaceLshift = (16, "<<="),
618        /// `@=`
619        InplaceMatrixMultiply = (17, "@="),
620        /// `*=`
621        InplaceMultiply = (18, "*="),
622        /// `%=`
623        InplaceRemainder = (19, "%="),
624        /// `|=`
625        InplaceOr = (20, "|="),
626        /// `**=`
627        InplacePower = (21, "**="),
628        /// `>>=`
629        InplaceRshift = (22, ">>="),
630        /// `-=`
631        InplaceSubtract = (23, "-="),
632        /// `/=`
633        InplaceTrueDivide = (24, "/="),
634        /// `^=`
635        InplaceXor = (25, "^="),
636        /// `[]` subscript
637        Subscr = (26, "[]"),
638    }
639);
640
641impl BinaryOperator {
642    /// Get the "inplace" version of the operator.
643    /// This has no effect if `self` is already an "inplace" operator.
644    ///
645    /// # Example
646    /// ```rust
647    /// use rustpython_compiler_core::bytecode::BinaryOperator;
648    ///
649    /// assert_eq!(BinaryOperator::Power.as_inplace(), BinaryOperator::InplacePower);
650    ///
651    /// assert_eq!(BinaryOperator::InplaceSubtract.as_inplace(), BinaryOperator::InplaceSubtract);
652    /// ```
653    #[must_use]
654    pub const fn as_inplace(self) -> Self {
655        match self {
656            Self::Add => Self::InplaceAdd,
657            Self::And => Self::InplaceAnd,
658            Self::FloorDivide => Self::InplaceFloorDivide,
659            Self::Lshift => Self::InplaceLshift,
660            Self::MatrixMultiply => Self::InplaceMatrixMultiply,
661            Self::Multiply => Self::InplaceMultiply,
662            Self::Remainder => Self::InplaceRemainder,
663            Self::Or => Self::InplaceOr,
664            Self::Power => Self::InplacePower,
665            Self::Rshift => Self::InplaceRshift,
666            Self::Subtract => Self::InplaceSubtract,
667            Self::TrueDivide => Self::InplaceTrueDivide,
668            Self::Xor => Self::InplaceXor,
669            _ => self,
670        }
671    }
672
673    /// https://github.com/python/cpython/blob/v3.14.4/Include/opcode.h#L10-L36
674    #[must_use]
675    pub const fn desc(&self) -> &str {
676        match self {
677            Self::Add => "NB_ADD",
678            Self::And => "NB_AND",
679            Self::FloorDivide => "NB_FLOOR_DIVIDE",
680            Self::Lshift => "NB_LSHIFT",
681            Self::MatrixMultiply => "NB_MATRIX_MULTIPLY",
682            Self::Multiply => "NB_MULTIPLY",
683            Self::Remainder => "NB_REMAINDER",
684            Self::Or => "NB_OR",
685            Self::Power => "NB_POWER",
686            Self::Rshift => "NB_RSHIFT",
687            Self::Subtract => "NB_SUBTRACT",
688            Self::TrueDivide => "NB_TRUE_DIVIDE",
689            Self::Xor => "NB_XOR",
690            Self::InplaceAdd => "NB_INPLACE_ADD",
691            Self::InplaceAnd => "NB_INPLACE_AND",
692            Self::InplaceFloorDivide => "NB_INPLACE_FLOOR_DIVIDE",
693            Self::InplaceLshift => "NB_INPLACE_LSHIFT",
694            Self::InplaceMatrixMultiply => "NB_INPLACE_MATRIX_MULTIPLY",
695            Self::InplaceMultiply => "NB_INPLACE_MULTIPLY",
696            Self::InplaceRemainder => "NB_INPLACE_REMAINDER",
697            Self::InplaceOr => "NB_INPLACE_OR",
698            Self::InplacePower => "NB_INPLACE_POWER",
699            Self::InplaceRshift => "NB_INPLACE_RSHIFT",
700            Self::InplaceSubtract => "NB_INPLACE_SUBTRACT",
701            Self::InplaceTrueDivide => "NB_INPLACE_TRUE_DIVIDE",
702            Self::InplaceXor => "NB_INPLACE_XOR",
703            Self::Subscr => "NB_SUBSCR",
704        }
705    }
706}
707
708oparg_enum!(
709    /// Whether or not to invert the operation.
710    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
711    pub enum Invert {
712        /// ```py
713        /// foo is bar
714        /// x in lst
715        /// ```
716        No = 0,
717        /// ```py
718        /// foo is not bar
719        /// x not in lst
720        /// ```
721        Yes = 1,
722    }
723);
724
725oparg_enum!(
726    /// Special method for LOAD_SPECIAL opcode (context managers).
727    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
728    pub enum SpecialMethod {
729        /// `__enter__` for sync context manager
730        Enter = (0, "__enter__"),
731        /// `__exit__` for sync context manager
732        Exit = (1, "__exit__"),
733        /// `__aenter__` for async context manager
734        AEnter = (2, "__aenter__"),
735        /// `__aexit__` for async context manager
736        AExit = (3, "__aexit__"),
737    }
738);
739
740oparg_enum!(
741    /// Common constants for LOAD_COMMON_CONSTANT opcode.
742    /// pycore_opcode_utils.h CONSTANT_*
743    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
744    pub enum CommonConstant {
745        /// `AssertionError` exception type
746        AssertionError = (0, "AssertionError"),
747        /// `NotImplementedError` exception type
748        NotImplementedError = (1, "NotImplementedError"),
749        /// Built-in `tuple` type
750        BuiltinTuple = (2, "tuple"),
751        /// Built-in `all` function
752        BuiltinAll = (3, "all"),
753        /// Built-in `any` function
754        BuiltinAny = (4, "any"),
755        /// Built-in `list` type
756        BuiltinList = (5, "list"),
757        /// Built-in `set` type
758        BuiltinSet = (6, "set"),
759    }
760);
761
762oparg_enum!(
763    /// Specifies if a slice is built with either 2 or 3 arguments.
764    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
765    pub enum BuildSliceArgCount {
766        /// ```py
767        /// x[5:10]
768        /// ```
769        Two = 2,
770        /// ```py
771        /// x[5:10:2]
772        /// ```
773        Three = 3,
774    }
775);
776
777#[derive(Copy, Clone)]
778pub struct UnpackExArgs {
779    pub before: u8,
780    pub after: u32,
781}
782
783impl From<u32> for UnpackExArgs {
784    fn from(value: u32) -> Self {
785        let before = (value & 0xFF) as u8;
786        let after = value >> 8;
787        Self { before, after }
788    }
789}
790
791impl From<UnpackExArgs> for u32 {
792    fn from(value: UnpackExArgs) -> Self {
793        Self::from(value.before) | (value.after << 8)
794    }
795}
796
797impl OpArgType for UnpackExArgs {}
798
799impl fmt::Display for UnpackExArgs {
800    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801        write!(f, "before: {}, after: {}", self.before, self.after)
802    }
803}
804
805macro_rules! newtype_oparg {
806    (
807      $(#[$oparg_meta:meta])*
808      $vis:vis struct $name:ident(u32)
809    ) => {
810        $(#[$oparg_meta])*
811        $vis struct $name(u32);
812
813        impl $name {
814            #[doc = concat!("Creates a new [`", stringify!($name), "`] instance.")]
815            #[must_use]
816            pub const fn from_u32(value: u32) -> Self {
817                Self(value)
818            }
819
820            /// Returns the oparg as a [`u32`] value.
821            #[must_use]
822            pub const fn as_u32(self) -> u32 {
823                self.0
824            }
825
826            /// Returns the oparg as a [`usize`] value.
827            #[must_use]
828            pub const fn as_usize(self) -> usize {
829              self.0 as usize
830            }
831        }
832
833        impl From<u32> for $name {
834            fn from(value: u32) -> Self {
835                Self::from_u32(value)
836            }
837        }
838
839        impl From<$name> for u32 {
840            fn from(value: $name) -> Self {
841                value.as_u32()
842            }
843        }
844
845        impl From<$name> for usize {
846            fn from(value: $name) -> Self {
847                value.as_usize()
848            }
849        }
850
851        impl ::core::fmt::Display for $name {
852            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
853                self.0.fmt(f)
854            }
855        }
856
857        impl OpArgType for $name {}
858    }
859}
860
861newtype_oparg!(
862    #[derive(Clone, Copy)]
863    #[repr(transparent)]
864    pub struct ConstIdx(u32)
865);
866
867newtype_oparg!(
868    #[derive(Clone, Copy)]
869    #[repr(transparent)]
870    pub struct VarNum(u32)
871);
872
873newtype_oparg!(
874    #[derive(Clone, Copy)]
875    #[repr(transparent)]
876    pub struct VarNums(u32)
877);
878
879newtype_oparg!(
880    #[derive(Clone, Copy)]
881    #[repr(transparent)]
882    pub struct LoadAttr(u32)
883);
884
885newtype_oparg!(
886    #[derive(Clone, Copy)]
887    #[repr(transparent)]
888    pub struct LoadSuperAttr(u32)
889);
890
891newtype_oparg!(
892    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
893    #[repr(transparent)]
894    pub struct Label(u32)
895);
896
897newtype_oparg!(
898    /// Context for [`Instruction::Resume`].
899    ///
900    /// The oparg consists of two parts:
901    /// 1. [`ResumeContext::location`]: Indicates where the instruction occurs.
902    /// 2. [`ResumeContext::is_exception_depth1`]: Is the instruction is at except-depth 1.
903    #[derive(Clone, Copy)]
904    #[repr(transparent)]
905    pub struct ResumeContext(u32)
906);
907
908impl ResumeContext {
909    /// [CPython `RESUME_OPARG_LOCATION_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L84)
910    pub const LOCATION_MASK: u32 = 0x3;
911
912    /// [CPython `RESUME_OPARG_DEPTH1_MASK`](https://github.com/python/cpython/blob/v3.14.3/Include/internal/pycore_opcode_utils.h#L85)
913    pub const DEPTH1_MASK: u32 = 0x4;
914
915    #[must_use]
916    pub const fn new(location: ResumeLocation, is_exception_depth1: bool) -> Self {
917        let value = if is_exception_depth1 {
918            Self::DEPTH1_MASK
919        } else {
920            0
921        };
922
923        Self::from_u32(location.as_u32() | value)
924    }
925
926    /// Resume location is determined by [`Self::LOCATION_MASK`].
927    #[must_use]
928    pub fn location(&self) -> ResumeLocation {
929        // SAFETY: The mask should return a value that is in range.
930        unsafe { ResumeLocation::try_from(self.as_u32() & Self::LOCATION_MASK).unwrap_unchecked() }
931    }
932
933    /// True if the bit at [`Self::DEPTH1_MASK`] is on.
934    #[must_use]
935    pub const fn is_exception_depth1(&self) -> bool {
936        (self.as_u32() & Self::DEPTH1_MASK) != 0
937    }
938}
939
940#[derive(Copy, Clone)]
941pub enum ResumeLocation {
942    /// At the start of a function, which is neither a generator, coroutine nor an async generator.
943    AtFuncStart,
944    /// After a `yield` expression.
945    AfterYield,
946    /// After a `yield from` expression.
947    AfterYieldFrom,
948    /// After an `await` expression.
949    AfterAwait,
950}
951
952impl From<ResumeLocation> for ResumeContext {
953    fn from(location: ResumeLocation) -> Self {
954        Self::new(location, false)
955    }
956}
957
958impl TryFrom<u32> for ResumeLocation {
959    type Error = MarshalError;
960
961    fn try_from(value: u32) -> Result<Self, Self::Error> {
962        Ok(match value {
963            0 => Self::AtFuncStart,
964            1 => Self::AfterYield,
965            2 => Self::AfterYieldFrom,
966            3 => Self::AfterAwait,
967            _ => return Err(Self::Error::InvalidBytecode),
968        })
969    }
970}
971
972impl ResumeLocation {
973    #[must_use]
974    pub const fn as_u8(&self) -> u8 {
975        match self {
976            Self::AtFuncStart => 0,
977            Self::AfterYield => 1,
978            Self::AfterYieldFrom => 2,
979            Self::AfterAwait => 3,
980        }
981    }
982
983    #[must_use]
984    pub const fn as_u32(&self) -> u32 {
985        self.as_u8() as u32
986    }
987}
988
989impl From<ResumeLocation> for u8 {
990    fn from(location: ResumeLocation) -> Self {
991        location.as_u8()
992    }
993}
994
995impl From<ResumeLocation> for u32 {
996    fn from(location: ResumeLocation) -> Self {
997        location.as_u32()
998    }
999}
1000
1001impl VarNums {
1002    #[must_use]
1003    pub const fn idx_1(self) -> VarNum {
1004        VarNum::from_u32(self.0 >> 4)
1005    }
1006
1007    #[must_use]
1008    pub const fn idx_2(self) -> VarNum {
1009        VarNum::from_u32(self.0 & 15)
1010    }
1011
1012    #[must_use]
1013    pub const fn indexes(self) -> (VarNum, VarNum) {
1014        (self.idx_1(), self.idx_2())
1015    }
1016}
1017
1018impl LoadAttr {
1019    #[must_use]
1020    pub const fn new(name_idx: u32, is_method: bool) -> Self {
1021        Self::from_u32((name_idx << 1) | (is_method as u32))
1022    }
1023
1024    #[must_use]
1025    pub const fn name_idx(self) -> u32 {
1026        self.0 >> 1
1027    }
1028
1029    #[must_use]
1030    pub const fn is_method(self) -> bool {
1031        (self.0 & 1) == 1
1032    }
1033}
1034
1035impl LoadSuperAttr {
1036    #[must_use]
1037    pub const fn new(name_idx: u32, is_load_method: bool, has_class: bool) -> Self {
1038        Self::from_u32((name_idx << 2) | (is_load_method as u32) | ((has_class as u32) << 1))
1039    }
1040
1041    #[must_use]
1042    pub const fn name_idx(self) -> u32 {
1043        self.0 >> 2
1044    }
1045
1046    #[must_use]
1047    pub const fn is_load_method(self) -> bool {
1048        (self.0 & 1) == 1
1049    }
1050
1051    #[must_use]
1052    pub const fn has_class(self) -> bool {
1053        (self.0 & 2) == 2
1054    }
1055}