Skip to main content

rustpython_compiler_core/bytecode/
oparg.rs

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