Skip to main content

rustpython_compiler_core/
marshal.rs

1use crate::{OneIndexed, SourceLocation, bytecode::*};
2use alloc::{boxed::Box, vec::Vec};
3use core::convert::Infallible;
4use malachite_bigint::{BigInt, Sign};
5use num_complex::Complex64;
6use rustpython_wtf8::Wtf8;
7
8pub const FORMAT_VERSION: u32 = 5;
9
10#[derive(Clone, Copy, Debug)]
11pub enum MarshalError {
12    /// Unexpected End Of File
13    Eof,
14    /// Unexpected End Of File where an object was to start
15    EofObject,
16    /// A run of bytes that the data ends in the middle of
17    DataTooShort,
18    /// Invalid Bytecode
19    InvalidBytecode,
20    /// Invalid utf8 in string
21    InvalidUtf8,
22    /// Invalid source location
23    InvalidLocation,
24    /// Bad type marker
25    BadType,
26    /// A type marker no reader knows
27    UnknownType,
28    /// A back reference that names nothing
29    InvalidRef,
30    /// A marker that stands for no object at all
31    NullObject,
32    /// A container length that is negative or does not fit, named by what it counts
33    BadSize(&'static str),
34}
35
36impl core::fmt::Display for MarshalError {
37    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        match self {
39            Self::Eof => f.write_str("unexpected end of data"),
40            Self::EofObject => f.write_str("unexpected end of data where an object was expected"),
41            Self::DataTooShort => f.write_str("data ends in the middle of a value"),
42            Self::InvalidBytecode => f.write_str("invalid bytecode"),
43            Self::InvalidUtf8 => f.write_str("invalid utf8"),
44            Self::InvalidLocation => f.write_str("invalid source location"),
45            Self::BadType => f.write_str("bad type marker"),
46            Self::UnknownType => f.write_str("unknown type code"),
47            Self::InvalidRef => f.write_str("invalid reference"),
48            Self::NullObject => f.write_str("NULL object in marshal data for object"),
49            Self::BadSize(what) => write!(f, "{what} size out of range"),
50        }
51    }
52}
53
54impl From<core::str::Utf8Error> for MarshalError {
55    fn from(_: core::str::Utf8Error) -> Self {
56        Self::InvalidUtf8
57    }
58}
59
60impl core::error::Error for MarshalError {}
61
62type Result<T, E = MarshalError> = core::result::Result<T, E>;
63
64#[derive(Clone, Copy)]
65#[repr(u8)]
66enum Type {
67    Null = b'0',
68    None = b'N',
69    False = b'F',
70    True = b'T',
71    StopIter = b'S',
72    Ellipsis = b'.',
73    Int = b'i',
74    Int64 = b'I',
75    Long = b'l',
76    Float = b'g',
77    FloatStr = b'f',
78    ComplexStr = b'x',
79    Complex = b'y',
80    Bytes = b's',
81    Interned = b't',
82    Ref = b'r',
83    Tuple = b'(',
84    SmallTuple = b')',
85    List = b'[',
86    Dict = b'{',
87    Code = b'c',
88    Unicode = b'u',
89    Set = b'<',
90    FrozenSet = b'>',
91    Slice = b':',
92    Ascii = b'a',
93    AsciiInterned = b'A',
94    ShortAscii = b'z',
95    ShortAsciiInterned = b'Z',
96}
97
98impl TryFrom<u8> for Type {
99    type Error = MarshalError;
100
101    fn try_from(value: u8) -> Result<Self> {
102        Ok(match value {
103            b'0' => Self::Null,
104            b'N' => Self::None,
105            b'F' => Self::False,
106            b'T' => Self::True,
107            b'S' => Self::StopIter,
108            b'.' => Self::Ellipsis,
109            b'i' => Self::Int,
110            b'I' => Self::Int64,
111            b'l' => Self::Long,
112            b'f' => Self::FloatStr,
113            b'g' => Self::Float,
114            b'x' => Self::ComplexStr,
115            b'y' => Self::Complex,
116            b's' => Self::Bytes,
117            b't' => Self::Interned,
118            b'r' => Self::Ref,
119            b'(' => Self::Tuple,
120            b')' => Self::SmallTuple,
121            b'[' => Self::List,
122            b'{' => Self::Dict,
123            b'c' => Self::Code,
124            b'u' => Self::Unicode,
125            b'<' => Self::Set,
126            b'>' => Self::FrozenSet,
127            b':' => Self::Slice,
128            b'a' => Self::Ascii,
129            b'A' => Self::AsciiInterned,
130            b'z' => Self::ShortAscii,
131            b'Z' => Self::ShortAsciiInterned,
132            _ => return Err(MarshalError::UnknownType),
133        })
134    }
135}
136
137pub trait Read {
138    fn read_slice(&mut self, n: u32) -> Result<&[u8]>;
139
140    fn read_array<const N: usize>(&mut self) -> Result<&[u8; N]> {
141        self.read_slice(N as u32).map(|s| s.try_into().unwrap())
142    }
143
144    fn read_str(&mut self, len: u32) -> Result<&str> {
145        Ok(core::str::from_utf8(self.read_slice(len)?)?)
146    }
147
148    fn read_wtf8(&mut self, len: u32) -> Result<&Wtf8> {
149        Wtf8::from_bytes(self.read_slice(len)?).ok_or(MarshalError::InvalidUtf8)
150    }
151
152    fn read_u8(&mut self) -> Result<u8> {
153        // A single byte is not a run that got cut short: nothing came at all.
154        let byte = self.read_array().map_err(|_| MarshalError::Eof)?;
155        Ok(u8::from_le_bytes(*byte))
156    }
157
158    fn read_u16(&mut self) -> Result<u16> {
159        Ok(u16::from_le_bytes(*self.read_array()?))
160    }
161
162    fn read_u32(&mut self) -> Result<u32> {
163        Ok(u32::from_le_bytes(*self.read_array()?))
164    }
165
166    fn read_u64(&mut self) -> Result<u64> {
167        Ok(u64::from_le_bytes(*self.read_array()?))
168    }
169
170    /// A length, read the way `r_long` reads one: it is signed, so a value
171    /// with the top bit set is out of range rather than four billion items.
172    fn read_len(&mut self, what: &'static str) -> Result<usize> {
173        let len = self.read_u32()? as i32;
174        usize::try_from(len).map_err(|_| MarshalError::BadSize(what))
175    }
176}
177
178pub(crate) trait ReadBorrowed<'a>: Read {
179    fn read_slice_borrow(&mut self, n: u32) -> Result<&'a [u8]>;
180
181    fn read_str_borrow(&mut self, len: u32) -> Result<&'a str> {
182        Ok(core::str::from_utf8(self.read_slice_borrow(len)?)?)
183    }
184}
185
186impl Read for &[u8] {
187    fn read_slice(&mut self, n: u32) -> Result<&[u8]> {
188        self.read_slice_borrow(n)
189    }
190
191    fn read_array<const N: usize>(&mut self) -> Result<&[u8; N]> {
192        let (chunk, rest) = self
193            .split_first_chunk::<N>()
194            .ok_or(MarshalError::DataTooShort)?;
195        *self = rest;
196        Ok(chunk)
197    }
198}
199
200impl<'a> ReadBorrowed<'a> for &'a [u8] {
201    fn read_slice_borrow(&mut self, n: u32) -> Result<&'a [u8]> {
202        self.split_off(..n as usize)
203            .ok_or(MarshalError::DataTooShort)
204    }
205}
206
207pub struct Cursor<B> {
208    pub data: B,
209    pub position: usize,
210}
211
212impl<B: AsRef<[u8]>> Read for Cursor<B> {
213    fn read_slice(&mut self, n: u32) -> Result<&[u8]> {
214        let data = &self.data.as_ref()[self.position..];
215        let slice = data.get(..n as usize).ok_or(MarshalError::DataTooShort)?;
216        self.position += n as usize;
217        Ok(slice)
218    }
219}
220
221/// Deserialize a code object (CPython field order).
222pub fn deserialize_code<R: Read, Bag: ConstantBag>(
223    rdr: &mut R,
224    bag: Bag,
225) -> Result<CodeObject<Bag::Constant>> {
226    let mut refs: Vec<Option<Bag::Constant>> = Vec::new();
227    deserialize_code_inner(rdr, bag, MAX_MARSHAL_STACK_DEPTH, &mut refs)
228}
229
230/// Inner code-object deserializer that shares a ref table with caller.
231/// Used when decoding a code object embedded in another marshal stream so
232/// that TYPE_REF entries inside the code can resolve across nested values.
233fn deserialize_code_inner<R: Read, Bag: ConstantBag>(
234    rdr: &mut R,
235    bag: Bag,
236    depth: usize,
237    refs: &mut Vec<Option<Bag::Constant>>,
238) -> Result<CodeObject<Bag::Constant>> {
239    if depth == 0 {
240        return Err(MarshalError::InvalidBytecode);
241    }
242    // 1–5: scalar fields
243    let arg_count = rdr.read_u32()?;
244    let posonlyarg_count = rdr.read_u32()?;
245    let kwonlyarg_count = rdr.read_u32()?;
246    let max_stackdepth = rdr.read_u32()?;
247    let flags = CodeFlags::from_bits_truncate(rdr.read_u32()?);
248
249    // 6: co_code
250    let code_bytes = read_marshal_bytes(rdr, &bag, refs)?;
251
252    // 7: co_consts
253    let constants = read_marshal_const_tuple(rdr, bag, depth, refs)?;
254
255    // 8: co_names
256    let names = read_marshal_name_tuple(rdr, &bag, refs)?;
257
258    // 9: co_localsplusnames
259    let localsplusnames = read_marshal_str_vec(rdr, &bag, refs)?;
260
261    // 10: co_localspluskinds
262    let localspluskinds = read_marshal_bytes(rdr, &bag, refs)?;
263
264    // 11–13: filename, name, qualname
265    let source_path = bag.make_name(&read_marshal_str(rdr, &bag, refs)?);
266    let obj_name = bag.make_name(&read_marshal_str(rdr, &bag, refs)?);
267    let qualname = bag.make_name(&read_marshal_str(rdr, &bag, refs)?);
268
269    // 14: co_firstlineno
270    let first_line_raw = rdr.read_u32()? as i32;
271    let first_line_number = if first_line_raw > 0 {
272        OneIndexed::new(first_line_raw as usize)
273    } else {
274        None
275    };
276
277    // 15–16: linetable, exceptiontable
278    let linetable = read_marshal_bytes(rdr, &bag, refs)?.into_boxed_slice();
279    let exceptiontable = read_marshal_bytes(rdr, &bag, refs)?.into_boxed_slice();
280
281    // Split localsplusnames/kinds → varnames/cellvars/freevars
282    let lp = split_localplus(
283        &localsplusnames
284            .iter()
285            .map(|s| s.as_str())
286            .collect::<Vec<&str>>(),
287        &localspluskinds,
288        arg_count,
289        kwonlyarg_count,
290        flags,
291    )?;
292
293    // Bytecode already uses flat localsplus indices (no translation needed)
294    let instructions = CodeUnits::try_from(code_bytes.as_slice())?;
295    let locations = linetable_to_locations(&linetable, first_line_raw, instructions.len());
296
297    // Use original localspluskinds from marshal data (preserves CO_FAST_HIDDEN etc.)
298    let localspluskinds = localspluskinds.into_boxed_slice();
299
300    Ok(CodeObject {
301        instructions,
302        locations,
303        flags,
304        posonlyarg_count,
305        arg_count,
306        kwonlyarg_count,
307        source_path,
308        first_line_number,
309        max_stackdepth,
310        obj_name,
311        qualname,
312        constants,
313        names,
314        varnames: lp.varnames.iter().map(|s| bag.make_name(s)).collect(),
315        cellvars: lp.cellvars.iter().map(|s| bag.make_name(s)).collect(),
316        freevars: lp.freevars.iter().map(|s| bag.make_name(s)).collect(),
317        localspluskinds,
318        linetable,
319        exceptiontable,
320    })
321}
322
323/// Reserve a ref slot if `FLAG_REF` was present, returning its index.
324fn reserve_ref_slot<T>(has_flag: bool, refs: &mut Vec<Option<T>>) -> Option<usize> {
325    if has_flag {
326        let idx = refs.len();
327        refs.push(None);
328        Some(idx)
329    } else {
330        None
331    }
332}
333
334/// Resolve a TYPE_REF index, returning the previously stored value.
335fn resolve_ref<T: Clone>(idx: usize, refs: &[Option<T>]) -> Result<T> {
336    refs.get(idx)
337        .and_then(|v| v.clone())
338        .ok_or(MarshalError::InvalidRef)
339}
340
341/// Read a marshal bytes object (TYPE_STRING = b's'), resolving TYPE_REF
342/// and registering this read in the ref table when `FLAG_REF` is set.
343fn read_marshal_bytes<R: Read, Bag: ConstantBag>(
344    rdr: &mut R,
345    bag: &Bag,
346    refs: &mut Vec<Option<Bag::Constant>>,
347) -> Result<Vec<u8>> {
348    let raw = rdr.read_u8()?;
349    let type_byte = raw & !FLAG_REF;
350    let has_flag = raw & FLAG_REF != 0;
351
352    if type_byte == Type::Ref as u8 {
353        let idx = rdr.read_u32()? as usize;
354        let stored = resolve_ref(idx, refs)?;
355        return match stored.borrow_constant() {
356            BorrowedConstant::Bytes { value } => Ok(value.to_vec()),
357            _ => Err(MarshalError::BadType),
358        };
359    }
360
361    if type_byte != Type::Bytes as u8 {
362        return Err(MarshalError::BadType);
363    }
364
365    let slot = reserve_ref_slot(has_flag, refs);
366    let len = rdr.read_u32()?;
367    let bytes = rdr.read_slice(len)?.to_vec();
368    if let Some(idx) = slot {
369        refs[idx] =
370            Some(bag.make_constant::<Bag::Constant>(BorrowedConstant::Bytes { value: &bytes }));
371    }
372    Ok(bytes)
373}
374
375/// Read a marshal string object, resolving TYPE_REF and registering
376/// this read in the ref table when `FLAG_REF` is set.
377fn read_marshal_str<R: Read, Bag: ConstantBag>(
378    rdr: &mut R,
379    bag: &Bag,
380    refs: &mut Vec<Option<Bag::Constant>>,
381) -> Result<alloc::string::String> {
382    let raw = rdr.read_u8()?;
383    let type_byte = raw & !FLAG_REF;
384    let has_flag = raw & FLAG_REF != 0;
385
386    if type_byte == Type::Ref as u8 {
387        let idx = rdr.read_u32()? as usize;
388        let stored = resolve_ref(idx, refs)?;
389        return match stored.borrow_constant() {
390            BorrowedConstant::Str { value } => Ok(value.to_string_lossy().into_owned()),
391            _ => Err(MarshalError::BadType),
392        };
393    }
394
395    let slot = reserve_ref_slot(has_flag, refs);
396    let owned = match type_byte {
397        b'u' | b't' | b'a' | b'A' => {
398            let len = rdr.read_u32()?;
399            alloc::string::String::from(rdr.read_str(len)?)
400        }
401        b'z' | b'Z' => {
402            let len = rdr.read_u8()? as u32;
403            alloc::string::String::from(rdr.read_str(len)?)
404        }
405        _ => return Err(MarshalError::BadType),
406    };
407    if let Some(idx) = slot {
408        refs[idx] = Some(bag.make_constant::<Bag::Constant>(BorrowedConstant::Str {
409            value: Wtf8::new(owned.as_str()),
410        }));
411    }
412    Ok(owned)
413}
414
415/// Read a marshal tuple of strings, returning owned Strings.
416fn read_marshal_str_vec<R: Read, Bag: ConstantBag>(
417    rdr: &mut R,
418    bag: &Bag,
419    refs: &mut Vec<Option<Bag::Constant>>,
420) -> Result<Vec<alloc::string::String>> {
421    let raw = rdr.read_u8()?;
422    let type_byte = raw & !FLAG_REF;
423    let has_flag = raw & FLAG_REF != 0;
424
425    if type_byte == Type::Ref as u8 {
426        let idx = rdr.read_u32()? as usize;
427        let stored = resolve_ref(idx, refs)?;
428        return match stored.borrow_constant() {
429            BorrowedConstant::Tuple { elements } => elements
430                .iter()
431                .map(|c| match c.borrow_constant() {
432                    BorrowedConstant::Str { value } => Ok(value.to_string_lossy().into_owned()),
433                    _ => Err(MarshalError::BadType),
434                })
435                .collect(),
436            _ => Err(MarshalError::BadType),
437        };
438    }
439
440    let n = match type_byte {
441        b'(' => rdr.read_len("tuple")?,
442        b')' => rdr.read_u8()? as usize,
443        _ => return Err(MarshalError::BadType),
444    };
445    let slot = reserve_ref_slot(has_flag, refs);
446    let items: Vec<alloc::string::String> = (0..n)
447        .map(|_| read_marshal_str(rdr, bag, refs))
448        .collect::<Result<_>>()?;
449    if let Some(idx) = slot {
450        let elements: Vec<Bag::Constant> = items
451            .iter()
452            .map(|s| {
453                bag.make_constant::<Bag::Constant>(BorrowedConstant::Str {
454                    value: Wtf8::new(s.as_str()),
455                })
456            })
457            .collect();
458        refs[idx] = Some(bag.make_constant::<Bag::Constant>(BorrowedConstant::Tuple {
459            elements: &elements,
460        }));
461    }
462    Ok(items)
463}
464
465fn read_marshal_name_tuple<R: Read, Bag: ConstantBag>(
466    rdr: &mut R,
467    bag: &Bag,
468    refs: &mut Vec<Option<Bag::Constant>>,
469) -> Result<Box<[<Bag::Constant as Constant>::Name]>> {
470    let names = read_marshal_str_vec(rdr, bag, refs)?;
471    Ok(names
472        .iter()
473        .map(|s| bag.make_name(s))
474        .collect::<Vec<_>>()
475        .into_boxed_slice())
476}
477
478/// Read a marshal tuple of constants. Shares the ref table with the
479/// surrounding code-object decode so that nested TYPE_REF entries (for
480/// strings, bytes, code objects, etc.) resolve correctly.
481fn read_marshal_const_tuple<R: Read, Bag: ConstantBag>(
482    rdr: &mut R,
483    bag: Bag,
484    depth: usize,
485    refs: &mut Vec<Option<Bag::Constant>>,
486) -> Result<Constants<Bag::Constant>> {
487    if depth == 0 {
488        return Err(MarshalError::InvalidBytecode);
489    }
490    let raw = rdr.read_u8()?;
491    let type_byte = raw & !FLAG_REF;
492    let has_flag = raw & FLAG_REF != 0;
493
494    if type_byte == Type::Ref as u8 {
495        let idx = rdr.read_u32()? as usize;
496        let stored = resolve_ref(idx, refs)?;
497        return match stored.borrow_constant() {
498            BorrowedConstant::Tuple { elements } => Ok(elements.iter().cloned().collect()),
499            _ => Err(MarshalError::BadType),
500        };
501    }
502
503    let n = match type_byte {
504        b'(' => rdr.read_len("tuple")?,
505        b')' => rdr.read_u8()? as usize,
506        _ => return Err(MarshalError::BadType),
507    };
508    let slot = reserve_ref_slot(has_flag, refs);
509    let child_depth = depth - 1;
510    let items: Vec<Bag::Constant> = (0..n)
511        .map(|_| read_const_value(rdr, bag, child_depth, refs))
512        .collect::<Result<_>>()?;
513    if let Some(idx) = slot {
514        refs[idx] =
515            Some(bag.make_constant::<Bag::Constant>(BorrowedConstant::Tuple { elements: &items }));
516    }
517    Ok(items.into_iter().collect())
518}
519
520/// Read a single value while staying inside an existing code-object ref
521/// space. Unlike `deserialize_value_depth`, encountering `Type::Code`
522/// here reuses the caller's ref table instead of opening a fresh one —
523/// this matches CPython's single global ref space for objects nested
524/// inside a code object's const tuple.
525fn read_const_value<R: Read, Bag: ConstantBag>(
526    rdr: &mut R,
527    bag: Bag,
528    depth: usize,
529    refs: &mut Vec<Option<Bag::Constant>>,
530) -> Result<Bag::Constant> {
531    if depth == 0 {
532        return Err(MarshalError::InvalidBytecode);
533    }
534    let raw = rdr.read_u8()?;
535    let flag = raw & FLAG_REF != 0;
536    let type_code = raw & !FLAG_REF;
537
538    if type_code == Type::Ref as u8 {
539        let idx = rdr.read_u32()? as usize;
540        return resolve_ref(idx, refs);
541    }
542
543    let slot = reserve_ref_slot(flag, refs);
544    let typ = Type::try_from(type_code)?;
545    let value = if matches!(typ, Type::Code) {
546        let code = deserialize_code_inner(rdr, bag, depth - 1, refs)?;
547        bag.make_code(code)
548    } else {
549        deserialize_value_typed(rdr, bag, depth, refs, typ, slot)?
550    };
551    if let Some(idx) = slot {
552        refs[idx] = Some(value.clone());
553    }
554    Ok(value)
555}
556
557pub trait MarshalBag: Copy {
558    type Value: Clone;
559    type ConstantBag: ConstantBag;
560
561    fn make_bool(&self, value: bool) -> Self::Value;
562
563    fn make_none(&self) -> Self::Value;
564
565    fn make_ellipsis(&self) -> Self::Value;
566
567    fn make_float(&self, value: f64) -> Self::Value;
568
569    fn make_complex(&self, value: Complex64) -> Self::Value;
570
571    fn make_str(&self, value: &Wtf8) -> Self::Value;
572
573    fn make_interned_str(&self, value: &Wtf8) -> Self::Value {
574        self.make_str(value)
575    }
576
577    fn make_bytes(&self, value: &[u8]) -> Self::Value;
578
579    fn make_int(&self, value: BigInt) -> Self::Value;
580
581    fn make_tuple(&self, elements: impl Iterator<Item = Self::Value>) -> Self::Value;
582
583    fn make_code(
584        &self,
585        code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
586    ) -> Result<Self::Value>;
587
588    /// Construct a runtime code object while retaining the exact values read
589    /// from ``co_consts``. Compiler bags ignore this second channel; runtime
590    /// bags use it for marshalable values (lists, dicts, sets, recursive
591    /// containers) that their compiler constant representation cannot hold.
592    fn make_code_with_constants(
593        &self,
594        code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
595        _constants: Vec<Self::Value>,
596    ) -> Result<Self::Value> {
597        self.make_code(code)
598    }
599
600    /// Decode the public `co_code` byte string into the execution-oriented
601    /// instruction storage.
602    ///
603    /// Runtime code-object implementations may accept byte values that the
604    /// compiler's `Instruction` enum cannot represent, retaining those bytes
605    /// separately and substituting a non-executable placeholder here.  The
606    /// default remains the strict compiler representation.
607    fn code_units_from_bytes(&self, code_bytes: &[u8]) -> Result<CodeUnits> {
608        CodeUnits::try_from(code_bytes)
609    }
610
611    /// Construct a runtime code object while retaining both its exact public
612    /// `co_code` bytes and the exact values read from `co_consts`.
613    ///
614    /// The default ignores the redundant byte spelling because ordinary
615    /// compiler code is represented losslessly by `CodeUnits`.  Runtime bags
616    /// that accepted otherwise unrepresentable opcode bytes in
617    /// [`MarshalBag::code_units_from_bytes`] can preserve them here.
618    fn make_code_with_constants_and_bytes(
619        &self,
620        code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
621        constants: Vec<Self::Value>,
622        _code_bytes: Vec<u8>,
623    ) -> Result<Self::Value> {
624        self.make_code_with_constants(code, constants)
625    }
626
627    fn make_stop_iter(&self) -> Result<Self::Value>;
628
629    fn make_list(&self, it: impl Iterator<Item = Self::Value>) -> Result<Self::Value>;
630
631    fn make_set(&self, it: impl Iterator<Item = Self::Value>) -> Result<Self::Value>;
632
633    fn make_frozenset(&self, it: impl Iterator<Item = Self::Value>) -> Result<Self::Value>;
634
635    fn make_dict(
636        &self,
637        it: impl Iterator<Item = (Self::Value, Self::Value)>,
638    ) -> Result<Self::Value>;
639
640    /// Install partially-built containers in the marshal reference table
641    /// before reading their children, as CPython's `r_object()` does.
642    /// Runtime bags can opt in; constant bags retain collect-then-construct.
643    ///
644    /// `len` comes straight from the input and is only bounded by what a
645    /// length can hold, so a bag that opts in reports the room it cannot get
646    /// rather than taking it for granted.
647    fn make_tuple_placeholder(&self, _len: usize) -> Result<Option<Self::Value>> {
648        Ok(None)
649    }
650
651    fn set_tuple_item(
652        &self,
653        _tuple: &Self::Value,
654        _index: usize,
655        _value: Self::Value,
656    ) -> Result<()> {
657        Err(MarshalError::BadType)
658    }
659
660    fn make_list_placeholder(&self, _len: usize) -> Result<Option<Self::Value>> {
661        Ok(None)
662    }
663
664    fn set_list_item(&self, _list: &Self::Value, _index: usize, _value: Self::Value) -> Result<()> {
665        Err(MarshalError::BadType)
666    }
667
668    fn make_set_placeholder(&self) -> Option<Self::Value> {
669        None
670    }
671
672    fn insert_set_item(&self, _set: &Self::Value, _value: Self::Value) -> Result<()> {
673        Err(MarshalError::BadType)
674    }
675
676    fn make_dict_placeholder(&self) -> Option<Self::Value> {
677        None
678    }
679
680    fn insert_dict_item(
681        &self,
682        _dict: &Self::Value,
683        _key: Self::Value,
684        _value: Self::Value,
685    ) -> Result<()> {
686        Err(MarshalError::BadType)
687    }
688
689    fn make_slice(
690        &self,
691        _start: Self::Value,
692        _stop: Self::Value,
693        _step: Self::Value,
694    ) -> Result<Self::Value> {
695        Err(MarshalError::BadType)
696    }
697
698    fn constant_bag(self) -> Self::ConstantBag;
699
700    fn constant_ref_from_value(
701        &self,
702        _value: &Self::Value,
703    ) -> Option<<Self::ConstantBag as ConstantBag>::Constant> {
704        None
705    }
706
707    /// Convert a runtime constant to the compiler-side shape stored in
708    /// ``CodeObject``. Runtime implementations may return a semantically
709    /// unused placeholder when the exact value is carried by
710    /// `make_code_with_constants` instead.
711    fn code_constant_from_value(
712        &self,
713        value: &Self::Value,
714    ) -> Result<<Self::ConstantBag as ConstantBag>::Constant> {
715        self.constant_ref_from_value(value)
716            .ok_or(MarshalError::BadType)
717    }
718
719    fn bytes_from_value(&self, _value: &Self::Value) -> Option<Vec<u8>> {
720        None
721    }
722
723    fn str_from_value(&self, _value: &Self::Value) -> Option<alloc::string::String> {
724        None
725    }
726
727    fn tuple_elements_from_value(&self, _value: &Self::Value) -> Option<Vec<Self::Value>> {
728        None
729    }
730}
731
732impl<Bag: ConstantBag> MarshalBag for Bag {
733    type Value = Bag::Constant;
734    type ConstantBag = Self;
735
736    fn make_bool(&self, value: bool) -> Self::Value {
737        self.make_constant::<Bag::Constant>(BorrowedConstant::Boolean { value })
738    }
739
740    fn make_none(&self) -> Self::Value {
741        self.make_constant::<Bag::Constant>(BorrowedConstant::None)
742    }
743
744    fn make_ellipsis(&self) -> Self::Value {
745        self.make_constant::<Bag::Constant>(BorrowedConstant::Ellipsis)
746    }
747
748    fn make_float(&self, value: f64) -> Self::Value {
749        self.make_constant::<Bag::Constant>(BorrowedConstant::Float { value })
750    }
751
752    fn make_complex(&self, value: Complex64) -> Self::Value {
753        self.make_constant::<Bag::Constant>(BorrowedConstant::Complex { value })
754    }
755
756    fn make_str(&self, value: &Wtf8) -> Self::Value {
757        self.make_constant::<Bag::Constant>(BorrowedConstant::Str { value })
758    }
759
760    fn make_bytes(&self, value: &[u8]) -> Self::Value {
761        self.make_constant::<Bag::Constant>(BorrowedConstant::Bytes { value })
762    }
763
764    fn make_int(&self, value: BigInt) -> Self::Value {
765        self.make_int(value)
766    }
767
768    fn make_tuple(&self, elements: impl Iterator<Item = Self::Value>) -> Self::Value {
769        self.make_tuple(elements)
770    }
771
772    fn make_slice(
773        &self,
774        start: Self::Value,
775        stop: Self::Value,
776        step: Self::Value,
777    ) -> Result<Self::Value> {
778        let elements = [start, stop, step];
779        Ok(
780            self.make_constant::<Bag::Constant>(BorrowedConstant::Slice {
781                elements: &elements,
782            }),
783        )
784    }
785
786    fn make_code(
787        &self,
788        code: CodeObject<<Self::ConstantBag as ConstantBag>::Constant>,
789    ) -> Result<Self::Value> {
790        Ok(self.make_code(code))
791    }
792
793    fn make_stop_iter(&self) -> Result<Self::Value> {
794        Err(MarshalError::BadType)
795    }
796
797    fn make_list(&self, _: impl Iterator<Item = Self::Value>) -> Result<Self::Value> {
798        Err(MarshalError::BadType)
799    }
800
801    fn make_set(&self, _: impl Iterator<Item = Self::Value>) -> Result<Self::Value> {
802        Err(MarshalError::BadType)
803    }
804
805    fn make_frozenset(&self, it: impl Iterator<Item = Self::Value>) -> Result<Self::Value> {
806        let elements: Vec<Self::Value> = it.collect();
807        Ok(
808            self.make_constant::<Bag::Constant>(BorrowedConstant::Frozenset {
809                elements: &elements,
810            }),
811        )
812    }
813
814    fn make_dict(
815        &self,
816        _: impl Iterator<Item = (Self::Value, Self::Value)>,
817    ) -> Result<Self::Value> {
818        Err(MarshalError::BadType)
819    }
820
821    fn constant_bag(self) -> Self::ConstantBag {
822        self
823    }
824
825    fn constant_ref_from_value(
826        &self,
827        value: &Self::Value,
828    ) -> Option<<Self::ConstantBag as ConstantBag>::Constant> {
829        Some(value.clone())
830    }
831
832    fn bytes_from_value(&self, value: &Self::Value) -> Option<Vec<u8>> {
833        match value.borrow_constant() {
834            BorrowedConstant::Bytes { value } => Some(value.to_vec()),
835            _ => None,
836        }
837    }
838
839    fn str_from_value(&self, value: &Self::Value) -> Option<alloc::string::String> {
840        match value.borrow_constant() {
841            BorrowedConstant::Str { value } => Some(value.to_string_lossy().into_owned()),
842            _ => None,
843        }
844    }
845
846    fn tuple_elements_from_value(&self, value: &Self::Value) -> Option<Vec<Self::Value>> {
847        match value.borrow_constant() {
848            BorrowedConstant::Tuple { elements } => Some(elements.to_vec()),
849            _ => None,
850        }
851    }
852}
853
854pub const MAX_MARSHAL_STACK_DEPTH: usize = 2000;
855
856pub fn deserialize_value<R: Read, Bag: MarshalBag>(rdr: &mut R, bag: Bag) -> Result<Bag::Value> {
857    let mut refs: Vec<Option<Bag::Value>> = Vec::new();
858    deserialize_value_depth(rdr, bag, MAX_MARSHAL_STACK_DEPTH, &mut refs)
859}
860
861/// Read the byte an object starts with. Running out of data here names the
862/// object that never came, rather than a field that was cut short.
863fn read_object_type<R: Read>(rdr: &mut R) -> Result<u8> {
864    rdr.read_u8().map_err(|_| MarshalError::EofObject)
865}
866
867fn deserialize_value_depth<R: Read, Bag: MarshalBag>(
868    rdr: &mut R,
869    bag: Bag,
870    depth: usize,
871    refs: &mut Vec<Option<Bag::Value>>,
872) -> Result<Bag::Value> {
873    if depth == 0 {
874        return Err(MarshalError::InvalidBytecode);
875    }
876    let raw = read_object_type(rdr)?;
877    deserialize_value_after_header(rdr, bag, depth, refs, raw)
878}
879
880/// Continue deserializing a value after the header byte has already been
881/// consumed. Shared by `deserialize_value_depth` and the dict-key branch,
882/// where the header byte is read up front to detect the TYPE_NULL
883/// terminator.
884fn deserialize_value_after_header<R: Read, Bag: MarshalBag>(
885    rdr: &mut R,
886    bag: Bag,
887    depth: usize,
888    refs: &mut Vec<Option<Bag::Value>>,
889    raw: u8,
890) -> Result<Bag::Value> {
891    if depth == 0 {
892        return Err(MarshalError::InvalidBytecode);
893    }
894    let flag = raw & FLAG_REF != 0;
895    let type_code = raw & !FLAG_REF;
896
897    // TYPE_REF: return previously stored object
898    if type_code == Type::Ref as u8 {
899        let idx = rdr.read_u32()? as usize;
900        return resolve_ref(idx, refs);
901    }
902
903    // Reserve ref slot before reading (matches write order)
904    let slot = if flag {
905        let idx = refs.len();
906        refs.push(None);
907        Some(idx)
908    } else {
909        None
910    };
911
912    let typ = Type::try_from(type_code)?;
913    let value = if matches!(typ, Type::Code) {
914        deserialize_code_value_inner(rdr, bag, depth - 1, refs)?
915    } else {
916        deserialize_value_typed(rdr, bag, depth, refs, typ, slot)?
917    };
918
919    if let Some(idx) = slot {
920        refs[idx] = Some(value.clone());
921    }
922    Ok(value)
923}
924
925/// Decode a code object through the runtime bag.  CPython's marshal reader
926/// keeps one reference table for the code fields and `co_consts`; using
927/// `Bag::Value` here preserves that index space and lets runtime-only
928/// constants survive alongside the compiler representation.
929fn deserialize_code_value_inner<R: Read, Bag: MarshalBag>(
930    rdr: &mut R,
931    bag: Bag,
932    depth: usize,
933    refs: &mut Vec<Option<Bag::Value>>,
934) -> Result<Bag::Value> {
935    if depth == 0 {
936        return Err(MarshalError::InvalidBytecode);
937    }
938    let arg_count = rdr.read_u32()?;
939    let posonlyarg_count = rdr.read_u32()?;
940    let kwonlyarg_count = rdr.read_u32()?;
941    let max_stackdepth = rdr.read_u32()?;
942    let flags = CodeFlags::from_bits_truncate(rdr.read_u32()?);
943    let child_depth = depth - 1;
944
945    let code_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
946    let code_bytes = bag
947        .bytes_from_value(&code_value)
948        .ok_or(MarshalError::BadType)?;
949
950    let consts_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
951    let constant_values = bag
952        .tuple_elements_from_value(&consts_value)
953        .ok_or(MarshalError::BadType)?;
954    let constants = constant_values
955        .iter()
956        .map(|value| bag.code_constant_from_value(value))
957        .collect::<Result<Vec<_>>>()?
958        .into_iter()
959        .collect();
960
961    let read_strings =
962        |rdr: &mut R, refs: &mut Vec<Option<Bag::Value>>| -> Result<Vec<alloc::string::String>> {
963            let tuple = deserialize_value_depth(rdr, bag, child_depth, refs)?;
964            bag.tuple_elements_from_value(&tuple)
965                .ok_or(MarshalError::BadType)?
966                .iter()
967                .map(|value| bag.str_from_value(value).ok_or(MarshalError::BadType))
968                .collect()
969        };
970    let names_raw = read_strings(rdr, refs)?;
971    let localsplusnames = read_strings(rdr, refs)?;
972
973    let kinds_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
974    let localspluskinds = bag
975        .bytes_from_value(&kinds_value)
976        .ok_or(MarshalError::BadType)?;
977
978    let read_string =
979        |rdr: &mut R, refs: &mut Vec<Option<Bag::Value>>| -> Result<alloc::string::String> {
980            let value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
981            bag.str_from_value(&value).ok_or(MarshalError::BadType)
982        };
983    let source_path_raw = read_string(rdr, refs)?;
984    let obj_name_raw = read_string(rdr, refs)?;
985    let qualname_raw = read_string(rdr, refs)?;
986
987    let first_line_raw = rdr.read_u32()? as i32;
988    let first_line_number = if first_line_raw > 0 {
989        OneIndexed::new(first_line_raw as usize)
990    } else {
991        None
992    };
993    let linetable_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
994    let linetable = bag
995        .bytes_from_value(&linetable_value)
996        .ok_or(MarshalError::BadType)?
997        .into_boxed_slice();
998    let exceptiontable_value = deserialize_value_depth(rdr, bag, child_depth, refs)?;
999    let exceptiontable = bag
1000        .bytes_from_value(&exceptiontable_value)
1001        .ok_or(MarshalError::BadType)?
1002        .into_boxed_slice();
1003
1004    let lp = split_localplus(
1005        &localsplusnames
1006            .iter()
1007            .map(|s| s.as_str())
1008            .collect::<Vec<_>>(),
1009        &localspluskinds,
1010        arg_count,
1011        kwonlyarg_count,
1012        flags,
1013    )?;
1014    let instructions = bag.code_units_from_bytes(&code_bytes)?;
1015    let locations = linetable_to_locations(&linetable, first_line_raw, instructions.len());
1016    let constant_bag = bag.constant_bag();
1017    let code = CodeObject {
1018        instructions,
1019        locations,
1020        flags,
1021        posonlyarg_count,
1022        arg_count,
1023        kwonlyarg_count,
1024        source_path: constant_bag.make_name(&source_path_raw),
1025        first_line_number,
1026        max_stackdepth,
1027        obj_name: constant_bag.make_name(&obj_name_raw),
1028        qualname: constant_bag.make_name(&qualname_raw),
1029        constants,
1030        names: names_raw
1031            .iter()
1032            .map(|name| constant_bag.make_name(name))
1033            .collect(),
1034        varnames: lp
1035            .varnames
1036            .iter()
1037            .map(|name| constant_bag.make_name(name))
1038            .collect(),
1039        cellvars: lp
1040            .cellvars
1041            .iter()
1042            .map(|name| constant_bag.make_name(name))
1043            .collect(),
1044        freevars: lp
1045            .freevars
1046            .iter()
1047            .map(|name| constant_bag.make_name(name))
1048            .collect(),
1049        localspluskinds: localspluskinds.into_boxed_slice(),
1050        linetable,
1051        exceptiontable,
1052    };
1053    bag.make_code_with_constants_and_bytes(code, constant_values, code_bytes)
1054}
1055
1056fn deserialize_value_typed<R: Read, Bag: MarshalBag>(
1057    rdr: &mut R,
1058    bag: Bag,
1059    depth: usize,
1060    refs: &mut Vec<Option<Bag::Value>>,
1061    typ: Type,
1062    slot: Option<usize>,
1063) -> Result<Bag::Value> {
1064    if depth == 0 {
1065        return Err(MarshalError::InvalidBytecode);
1066    }
1067    let value = match typ {
1068        Type::True => bag.make_bool(true),
1069        Type::False => bag.make_bool(false),
1070        Type::None => bag.make_none(),
1071        Type::StopIter => bag.make_stop_iter()?,
1072        Type::Ellipsis => bag.make_ellipsis(),
1073        Type::Int => {
1074            let val = rdr.read_u32()? as i32;
1075            bag.make_int(BigInt::from(val))
1076        }
1077        Type::Int64 => {
1078            let lo = rdr.read_u32()? as u64;
1079            let hi = rdr.read_u32()? as u64;
1080            bag.make_int(BigInt::from(((hi << 32) | lo) as i64))
1081        }
1082        Type::Long => bag.make_int(read_pylong(rdr)?),
1083        Type::FloatStr => bag.make_float(read_float_str(rdr)?),
1084        Type::Float => {
1085            let value = f64::from_bits(rdr.read_u64()?);
1086            bag.make_float(value)
1087        }
1088        Type::ComplexStr => {
1089            let re = read_float_str(rdr)?;
1090            let im = read_float_str(rdr)?;
1091            bag.make_complex(Complex64 { re, im })
1092        }
1093        Type::Complex => {
1094            let re = f64::from_bits(rdr.read_u64()?);
1095            let im = f64::from_bits(rdr.read_u64()?);
1096            let value = Complex64 { re, im };
1097            bag.make_complex(value)
1098        }
1099        Type::Ascii | Type::Unicode => {
1100            let len = rdr.read_len("string")?;
1101            let value = rdr.read_wtf8(len as u32)?;
1102            bag.make_str(value)
1103        }
1104        Type::AsciiInterned | Type::Interned => {
1105            let len = rdr.read_len("string")?;
1106            let value = rdr.read_wtf8(len as u32)?;
1107            bag.make_interned_str(value)
1108        }
1109        Type::ShortAscii => {
1110            let len = rdr.read_u8()? as u32;
1111            let value = rdr.read_wtf8(len)?;
1112            bag.make_str(value)
1113        }
1114        Type::ShortAsciiInterned => {
1115            let len = rdr.read_u8()? as u32;
1116            let value = rdr.read_wtf8(len)?;
1117            bag.make_interned_str(value)
1118        }
1119        Type::SmallTuple => {
1120            let len = rdr.read_u8()? as usize;
1121            let d = depth - 1;
1122            if let Some(index) = slot
1123                && let Some(tuple) = bag.make_tuple_placeholder(len)?
1124            {
1125                refs[index] = Some(tuple.clone());
1126                for item_index in 0..len {
1127                    let item = deserialize_value_depth(rdr, bag, d, refs)?;
1128                    bag.set_tuple_item(&tuple, item_index, item)?;
1129                }
1130                tuple
1131            } else {
1132                let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
1133                itertools::process_results(it, |it| bag.make_tuple(it))?
1134            }
1135        }
1136        Type::Null => {
1137            return Err(MarshalError::NullObject);
1138        }
1139        Type::Ref => {
1140            // Handled in deserialize_value_depth before calling this function
1141            return Err(MarshalError::BadType);
1142        }
1143        Type::Tuple => {
1144            let len = rdr.read_len("tuple")?;
1145            let d = depth - 1;
1146            if let Some(index) = slot
1147                && let Some(tuple) = bag.make_tuple_placeholder(len)?
1148            {
1149                refs[index] = Some(tuple.clone());
1150                for item_index in 0..len {
1151                    let item = deserialize_value_depth(rdr, bag, d, refs)?;
1152                    bag.set_tuple_item(&tuple, item_index, item)?;
1153                }
1154                tuple
1155            } else {
1156                let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
1157                itertools::process_results(it, |it| bag.make_tuple(it))?
1158            }
1159        }
1160        Type::List => {
1161            let len = rdr.read_len("list")?;
1162            let d = depth - 1;
1163            if let Some(index) = slot
1164                && let Some(list) = bag.make_list_placeholder(len)?
1165            {
1166                refs[index] = Some(list.clone());
1167                for item_index in 0..len {
1168                    let item = deserialize_value_depth(rdr, bag, d, refs)?;
1169                    bag.set_list_item(&list, item_index, item)?;
1170                }
1171                list
1172            } else {
1173                let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
1174                itertools::process_results(it, |it| bag.make_list(it))??
1175            }
1176        }
1177        Type::Set => {
1178            let len = rdr.read_len("set")?;
1179            let d = depth - 1;
1180            if let Some(index) = slot
1181                && let Some(set) = bag.make_set_placeholder()
1182            {
1183                refs[index] = Some(set.clone());
1184                for _ in 0..len {
1185                    let item = deserialize_value_depth(rdr, bag, d, refs)?;
1186                    bag.insert_set_item(&set, item)?;
1187                }
1188                set
1189            } else {
1190                let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
1191                itertools::process_results(it, |it| bag.make_set(it))??
1192            }
1193        }
1194        Type::FrozenSet => {
1195            let len = rdr.read_len("set")?;
1196            let d = depth - 1;
1197            let it = (0..len).map(|_| deserialize_value_depth(rdr, bag, d, refs));
1198            itertools::process_results(it, |it| bag.make_frozenset(it))??
1199        }
1200        Type::Dict => {
1201            let d = depth - 1;
1202            if let Some(index) = slot
1203                && let Some(dict) = bag.make_dict_placeholder()
1204            {
1205                refs[index] = Some(dict.clone());
1206                loop {
1207                    let raw = read_object_type(rdr)?;
1208                    if raw & !FLAG_REF == b'0' {
1209                        break;
1210                    }
1211                    let key = deserialize_value_after_header(rdr, bag, d, refs, raw)?;
1212                    let value = deserialize_value_depth(rdr, bag, d, refs)?;
1213                    bag.insert_dict_item(&dict, key, value)?;
1214                }
1215                dict
1216            } else {
1217                let mut pairs = Vec::new();
1218                loop {
1219                    let raw = read_object_type(rdr)?;
1220                    if raw & !FLAG_REF == b'0' {
1221                        break;
1222                    }
1223                    let key = deserialize_value_after_header(rdr, bag, d, refs, raw)?;
1224                    let value = deserialize_value_depth(rdr, bag, d, refs)?;
1225                    pairs.push((key, value));
1226                }
1227                bag.make_dict(pairs.into_iter())?
1228            }
1229        }
1230        Type::Bytes => {
1231            // After marshaling, byte arrays are converted into bytes.
1232            let len = rdr.read_len("bytes object")?;
1233            let value = rdr.read_slice(len as u32)?;
1234            bag.make_bytes(value)
1235        }
1236        Type::Code => return Err(MarshalError::BadType),
1237        Type::Slice => {
1238            let d = depth - 1;
1239            let start = deserialize_value_depth(rdr, bag, d, refs)?;
1240            let stop = deserialize_value_depth(rdr, bag, d, refs)?;
1241            let step = deserialize_value_depth(rdr, bag, d, refs)?;
1242            bag.make_slice(start, stop, step)?
1243        }
1244    };
1245    Ok(value)
1246}
1247
1248pub trait Dumpable: Sized {
1249    type Error;
1250    type Constant: Constant;
1251
1252    fn with_dump<R>(&self, f: impl FnOnce(DumpableValue<'_, Self>) -> R) -> Result<R, Self::Error>;
1253}
1254
1255pub enum DumpableValue<'a, D: Dumpable> {
1256    Integer(&'a BigInt),
1257    Float(f64),
1258    Complex(Complex64),
1259    Boolean(bool),
1260    Str(&'a Wtf8),
1261    Bytes(&'a [u8]),
1262    Code(&'a CodeObject<D::Constant>),
1263    Tuple(&'a [D]),
1264    None,
1265    Ellipsis,
1266    StopIter,
1267    List(&'a [D]),
1268    Set(&'a [D]),
1269    Frozenset(&'a [D]),
1270    Dict(&'a [(D, D)]),
1271    Slice(&'a D, &'a D, &'a D),
1272}
1273
1274impl<'a, C: Constant> From<BorrowedConstant<'a, C>> for DumpableValue<'a, C> {
1275    fn from(c: BorrowedConstant<'a, C>) -> Self {
1276        match c {
1277            BorrowedConstant::Integer { value } => Self::Integer(value),
1278            BorrowedConstant::Float { value } => Self::Float(value),
1279            BorrowedConstant::Complex { value } => Self::Complex(value),
1280            BorrowedConstant::Boolean { value } => Self::Boolean(value),
1281            BorrowedConstant::Str { value } => Self::Str(value),
1282            BorrowedConstant::Bytes { value } => Self::Bytes(value),
1283            BorrowedConstant::Code { code } => Self::Code(code),
1284            BorrowedConstant::Tuple { elements } => Self::Tuple(elements),
1285            BorrowedConstant::Slice { elements } => {
1286                Self::Slice(&elements[0], &elements[1], &elements[2])
1287            }
1288            BorrowedConstant::Frozenset { elements } => Self::Frozenset(elements),
1289            BorrowedConstant::None => Self::None,
1290            BorrowedConstant::Ellipsis => Self::Ellipsis,
1291        }
1292    }
1293}
1294
1295impl<C: Constant> Dumpable for C {
1296    type Error = Infallible;
1297    type Constant = Self;
1298
1299    #[inline(always)]
1300    fn with_dump<R>(&self, f: impl FnOnce(DumpableValue<'_, Self>) -> R) -> Result<R, Self::Error> {
1301        Ok(f(self.borrow_constant().into()))
1302    }
1303}
1304
1305pub trait Write {
1306    fn write_slice(&mut self, slice: &[u8]);
1307
1308    fn write_u8(&mut self, v: u8) {
1309        self.write_slice(&v.to_le_bytes())
1310    }
1311
1312    fn write_u16(&mut self, v: u16) {
1313        self.write_slice(&v.to_le_bytes())
1314    }
1315
1316    fn write_u32(&mut self, v: u32) {
1317        self.write_slice(&v.to_le_bytes())
1318    }
1319
1320    fn write_u64(&mut self, v: u64) {
1321        self.write_slice(&v.to_le_bytes())
1322    }
1323}
1324
1325impl Write for Vec<u8> {
1326    fn write_slice(&mut self, slice: &[u8]) {
1327        self.extend_from_slice(slice)
1328    }
1329}
1330
1331pub(crate) fn write_len<W: Write>(buf: &mut W, len: usize) {
1332    let Ok(len) = len.try_into() else {
1333        panic!("too long to serialize")
1334    };
1335    buf.write_u32(len);
1336}
1337
1338pub(crate) fn write_vec<W: Write>(buf: &mut W, slice: &[u8]) {
1339    write_len(buf, slice.len());
1340    buf.write_slice(slice);
1341}
1342
1343pub fn serialize_value<W: Write, D: Dumpable>(
1344    buf: &mut W,
1345    constant: DumpableValue<'_, D>,
1346) -> Result<(), D::Error> {
1347    match constant {
1348        DumpableValue::Integer(int) => {
1349            if let Ok(val) = i32::try_from(int) {
1350                buf.write_u8(Type::Int as u8); // TYPE_INT: 4-byte LE i32
1351                buf.write_u32(val as u32);
1352            } else {
1353                buf.write_u8(Type::Long as u8);
1354                let (sign, raw) = int.to_bytes_le();
1355                let mut digits = alloc::vec::Vec::new();
1356                let mut accum: u32 = 0;
1357                let mut bits = 0u32;
1358                for &byte in &raw {
1359                    accum |= (byte as u32) << bits;
1360                    bits += 8;
1361                    while bits >= 15 {
1362                        digits.push((accum & 0x7fff) as u16);
1363                        accum >>= 15;
1364                        bits -= 15;
1365                    }
1366                }
1367                if accum > 0 || digits.is_empty() {
1368                    digits.push(accum as u16);
1369                }
1370                while digits.len() > 1 && *digits.last().unwrap() == 0 {
1371                    digits.pop();
1372                }
1373                let n = digits.len() as i32;
1374                let n = if sign == Sign::Minus { -n } else { n };
1375                buf.write_u32(n as u32);
1376                for d in &digits {
1377                    buf.write_u16(*d);
1378                }
1379            }
1380        }
1381        DumpableValue::Float(f) => {
1382            buf.write_u8(Type::Float as u8);
1383            buf.write_u64(f.to_bits());
1384        }
1385        DumpableValue::Complex(c) => {
1386            buf.write_u8(Type::Complex as u8);
1387            buf.write_u64(c.re.to_bits());
1388            buf.write_u64(c.im.to_bits());
1389        }
1390        DumpableValue::Boolean(b) => {
1391            buf.write_u8(if b { Type::True } else { Type::False } as u8);
1392        }
1393        DumpableValue::Str(s) => {
1394            buf.write_u8(Type::Unicode as u8);
1395            write_vec(buf, s.as_bytes());
1396        }
1397        DumpableValue::Bytes(b) => {
1398            buf.write_u8(Type::Bytes as u8);
1399            write_vec(buf, b);
1400        }
1401        DumpableValue::Code(c) => {
1402            buf.write_u8(Type::Code as u8);
1403            serialize_code(buf, c);
1404        }
1405        DumpableValue::Tuple(tup) => {
1406            buf.write_u8(Type::Tuple as u8);
1407            write_len(buf, tup.len());
1408            for val in tup {
1409                val.with_dump(|val| serialize_value(buf, val))??
1410            }
1411        }
1412        DumpableValue::None => {
1413            buf.write_u8(Type::None as u8);
1414        }
1415        DumpableValue::Ellipsis => {
1416            buf.write_u8(Type::Ellipsis as u8);
1417        }
1418        DumpableValue::StopIter => {
1419            buf.write_u8(Type::StopIter as u8);
1420        }
1421        DumpableValue::List(l) => {
1422            buf.write_u8(Type::List as u8);
1423            write_len(buf, l.len());
1424            for val in l {
1425                val.with_dump(|val| serialize_value(buf, val))??
1426            }
1427        }
1428        DumpableValue::Set(set) => {
1429            buf.write_u8(Type::Set as u8);
1430            write_len(buf, set.len());
1431            for val in set {
1432                val.with_dump(|val| serialize_value(buf, val))??
1433            }
1434        }
1435        DumpableValue::Frozenset(set) => {
1436            buf.write_u8(Type::FrozenSet as u8);
1437            write_len(buf, set.len());
1438            for val in set {
1439                val.with_dump(|val| serialize_value(buf, val))??
1440            }
1441        }
1442        DumpableValue::Dict(d) => {
1443            buf.write_u8(Type::Dict as u8);
1444            for (k, v) in d {
1445                k.with_dump(|val| serialize_value(buf, val))??;
1446                v.with_dump(|val| serialize_value(buf, val))??;
1447            }
1448            buf.write_u8(b'0'); // TYPE_NULL
1449        }
1450        DumpableValue::Slice(start, stop, step) => {
1451            buf.write_u8(Type::Slice as u8);
1452            start.with_dump(|val| serialize_value(buf, val))??;
1453            stop.with_dump(|val| serialize_value(buf, val))??;
1454            step.with_dump(|val| serialize_value(buf, val))??;
1455        }
1456    }
1457    Ok(())
1458}
1459
1460/// Serialize a code object in CPython field order.
1461///
1462/// Split varnames/cellvars/freevars are reassembled into
1463/// co_localsplusnames/co_localspluskinds.
1464pub fn serialize_code<W: Write, C: Constant>(buf: &mut W, code: &CodeObject<C>) {
1465    serialize_code_with(buf, code, |buf, constant| {
1466        serialize_value(buf, constant.borrow_constant().into()).unwrap_or_else(|x| match x {});
1467        Ok::<(), core::convert::Infallible>(())
1468    })
1469    .unwrap_or_else(|x| match x {})
1470}
1471
1472/// Serialize a code object, writing each `co_consts` entry through
1473/// `write_constant`.
1474///
1475/// A runtime caller passes its own object writer so that values its constant
1476/// representation carries but `BorrowedConstant` cannot describe — lists,
1477/// dicts, sets — reach the stream, and so a constant shared with the enclosing
1478/// object keeps its entry in that writer's reference table.
1479pub fn serialize_code_with<W: Write, C: Constant, E>(
1480    buf: &mut W,
1481    code: &CodeObject<C>,
1482    mut write_constant: impl FnMut(&mut W, &C) -> core::result::Result<(), E>,
1483) -> core::result::Result<(), E> {
1484    // 1–5: scalar fields
1485    buf.write_u32(code.arg_count);
1486    buf.write_u32(code.posonlyarg_count);
1487    buf.write_u32(code.kwonlyarg_count);
1488    buf.write_u32(code.max_stackdepth);
1489    buf.write_u32(code.flags.bits());
1490
1491    // 6: co_code (TYPE_STRING) — bytecode already uses flat localsplus indices
1492    let bytecode = code.instructions.original_bytes();
1493    buf.write_u8(Type::Bytes as u8);
1494    write_vec(buf, &bytecode);
1495
1496    // 7: co_consts (TYPE_TUPLE)
1497    buf.write_u8(Type::Tuple as u8);
1498    write_len(buf, code.constants.len());
1499    for constant in &*code.constants {
1500        write_constant(buf, constant)?;
1501    }
1502
1503    // 8: co_names (tuple of strings)
1504    write_marshal_name_tuple(buf, &code.names);
1505
1506    // 9: co_localsplusnames — varnames ++ cell_only ++ freevars
1507    let cell_only_names: Vec<&str> = code
1508        .cellvars
1509        .iter()
1510        .filter(|cv| !code.varnames.iter().any(|v| v.as_ref() == cv.as_ref()))
1511        .map(|cv| cv.as_ref())
1512        .collect();
1513    let total_lp_count = code.varnames.len() + cell_only_names.len() + code.freevars.len();
1514    buf.write_u8(Type::Tuple as u8);
1515    write_len(buf, total_lp_count);
1516    for n in &code.varnames {
1517        write_marshal_str(buf, n.as_ref());
1518    }
1519    for &n in &cell_only_names {
1520        write_marshal_str(buf, n);
1521    }
1522    for n in &code.freevars {
1523        write_marshal_str(buf, n.as_ref());
1524    }
1525    // 10: co_localspluskinds — use the stored kinds directly
1526    buf.write_u8(Type::Bytes as u8);
1527    write_vec(buf, &code.localspluskinds);
1528
1529    // 11: co_filename
1530    write_marshal_str(buf, code.source_path.as_ref());
1531    // 12: co_name
1532    write_marshal_str(buf, code.obj_name.as_ref());
1533    // 13: co_qualname
1534    write_marshal_str(buf, code.qualname.as_ref());
1535    // 14: co_firstlineno
1536    buf.write_u32(code.first_line_number.map_or(0, |x| x.get() as _));
1537    // 15: co_linetable
1538    buf.write_u8(Type::Bytes as u8);
1539    write_vec(buf, &code.linetable);
1540    // 16: co_exceptiontable
1541    buf.write_u8(Type::Bytes as u8);
1542    write_vec(buf, &code.exceptiontable);
1543    Ok(())
1544}
1545
1546fn write_marshal_str<W: Write>(buf: &mut W, s: &str) {
1547    let bytes = s.as_bytes();
1548    if bytes.len() < 256 && bytes.is_ascii() {
1549        buf.write_u8(b'z'); // TYPE_SHORT_ASCII
1550        buf.write_u8(bytes.len() as u8);
1551    } else {
1552        buf.write_u8(Type::Unicode as u8);
1553        write_len(buf, bytes.len());
1554    }
1555    buf.write_slice(bytes);
1556}
1557
1558fn write_marshal_name_tuple<W: Write, N: AsRef<str>>(buf: &mut W, names: &[N]) {
1559    buf.write_u8(Type::Tuple as u8);
1560    write_len(buf, names.len());
1561    for name in names {
1562        write_marshal_str(buf, name.as_ref());
1563    }
1564}
1565
1566pub const FLAG_REF: u8 = 0x80;
1567
1568/// Read a signed 32-bit LE integer.
1569pub fn read_i32<R: Read>(rdr: &mut R) -> Result<i32> {
1570    let bytes = rdr.read_array::<4>()?;
1571    Ok(i32::from_le_bytes(*bytes))
1572}
1573
1574/// Read a TYPE_LONG arbitrary-precision integer (base-2^15 digits).
1575pub fn read_pylong<R: Read>(rdr: &mut R) -> Result<BigInt> {
1576    const MARSHAL_SHIFT: u32 = 15;
1577    const MARSHAL_BASE: u32 = 1 << MARSHAL_SHIFT;
1578    let n = read_i32(rdr)?;
1579    if n == 0 {
1580        return Ok(BigInt::from(0));
1581    }
1582    let negative = n < 0;
1583    let num_digits = n.unsigned_abs() as usize;
1584    let mut accum = BigInt::from(0);
1585    let mut last_digit = 0u32;
1586    for i in 0..num_digits {
1587        let d = rdr.read_u16()? as u32;
1588        if d >= MARSHAL_BASE {
1589            return Err(MarshalError::InvalidBytecode);
1590        }
1591        last_digit = d;
1592        accum += BigInt::from(d) << (i as u32 * MARSHAL_SHIFT);
1593    }
1594    if num_digits > 0 && last_digit == 0 {
1595        return Err(MarshalError::InvalidBytecode);
1596    }
1597    if negative {
1598        accum = -accum;
1599    }
1600    Ok(accum)
1601}
1602
1603/// Read a text-encoded float (1-byte length + ASCII).
1604pub fn read_float_str<R: Read>(rdr: &mut R) -> Result<f64> {
1605    let n = rdr.read_u8()? as u32;
1606    let s = rdr.read_str(n)?;
1607    s.parse::<f64>().map_err(|_| MarshalError::InvalidBytecode)
1608}
1609
1610/// Read a 4-byte-length-prefixed byte string.
1611pub fn read_pstring<R: Read>(rdr: &mut R) -> Result<&[u8]> {
1612    let n = read_i32(rdr)?;
1613    if n < 0 {
1614        return Err(MarshalError::InvalidBytecode);
1615    }
1616    rdr.read_slice(n as u32)
1617}
1618
1619pub struct LocalsPlusResult<S> {
1620    pub varnames: Vec<S>,
1621    pub cellvars: Vec<S>,
1622    pub freevars: Vec<S>,
1623    pub cell2arg: Option<Box<[i32]>>,
1624    pub deref_map: Vec<u32>,
1625}
1626
1627pub fn split_localplus<S: Clone>(
1628    names: &[S],
1629    kinds: &[u8],
1630    arg_count: u32,
1631    kwonlyarg_count: u32,
1632    flags: CodeFlags,
1633) -> Result<LocalsPlusResult<S>> {
1634    if names.len() != kinds.len() {
1635        return Err(MarshalError::InvalidBytecode);
1636    }
1637
1638    let mut varnames = Vec::new();
1639    let mut cellvars = Vec::new();
1640    let mut freevars = Vec::new();
1641
1642    // First pass: collect varnames (LOCAL entries) and freevars
1643    for (name, &kind) in names.iter().zip(kinds.iter()) {
1644        let kind = CoFastFlags::from_bits_retain(kind);
1645        if kind.contains(&CoFastFlag::Local) {
1646            varnames.push(name.clone());
1647        }
1648        if kind.contains(&CoFastFlag::Free) {
1649            freevars.push(name.clone());
1650        }
1651    }
1652
1653    // Second pass: collect cellvars in localsplusnames order.
1654    // CELL-only vars come from non-LOCAL CELL entries.
1655    // LOCAL|CELL vars are also added to cellvars.
1656    // This preserves the original ordering from localsplusnames.
1657    let mut arg_cell_positions = Vec::new(); // (cell_idx, localplus_idx)
1658    for (i, (name, &kind)) in names.iter().zip(kinds.iter()).enumerate() {
1659        let kind = CoFastFlags::from_bits_retain(kind);
1660        let is_local = kind.contains(&CoFastFlag::Local);
1661        let is_cell = kind.contains(&CoFastFlag::Cell);
1662        if is_cell {
1663            let cell_idx = cellvars.len();
1664            cellvars.push(name.clone());
1665            if is_local {
1666                arg_cell_positions.push((cell_idx, i));
1667            }
1668        }
1669    }
1670
1671    let total_args = {
1672        let mut t = arg_count + kwonlyarg_count;
1673        if flags.contains(CodeFlags::VARARGS) {
1674            t += 1;
1675        }
1676        if flags.contains(CodeFlags::VARKEYWORDS) {
1677            t += 1;
1678        }
1679        t
1680    };
1681
1682    let cell2arg = if !cellvars.is_empty() {
1683        let mut mapping = alloc::vec![-1i32; cellvars.len()];
1684        for &(cell_idx, localplus_idx) in &arg_cell_positions {
1685            if (localplus_idx as u32) < total_args {
1686                mapping[cell_idx] = localplus_idx as i32;
1687            }
1688        }
1689        if mapping.iter().any(|&x| x >= 0) {
1690            Some(mapping.into_boxed_slice())
1691        } else {
1692            None
1693        }
1694    } else {
1695        None
1696    };
1697
1698    // Build deref_map: localsplusnames index → cellvar/freevar index
1699    let mut deref_map = alloc::vec![u32::MAX; names.len()];
1700    let mut cell_idx = 0u32;
1701    for (i, &kind) in kinds.iter().enumerate() {
1702        if CoFastFlags::from_bits_retain(kind).contains(&CoFastFlag::Cell) {
1703            deref_map[i] = cell_idx;
1704            cell_idx += 1;
1705        }
1706    }
1707    let ncells = cellvars.len();
1708    let mut free_idx = 0u32;
1709    for (i, &kind) in kinds.iter().enumerate() {
1710        if CoFastFlags::from_bits_retain(kind).contains(&CoFastFlag::Free) {
1711            deref_map[i] = ncells as u32 + free_idx;
1712            free_idx += 1;
1713        }
1714    }
1715
1716    Ok(LocalsPlusResult {
1717        varnames,
1718        cellvars,
1719        freevars,
1720        cell2arg,
1721        deref_map,
1722    })
1723}
1724
1725#[must_use]
1726pub fn linetable_to_locations(
1727    linetable: &[u8],
1728    first_line: i32,
1729    num_instructions: usize,
1730) -> Box<[(SourceLocation, SourceLocation)]> {
1731    let default_loc = || {
1732        let line = if first_line > 0 {
1733            OneIndexed::new(first_line as usize).unwrap_or(OneIndexed::MIN)
1734        } else {
1735            OneIndexed::MIN
1736        };
1737        let loc = SourceLocation {
1738            line,
1739            character_offset: OneIndexed::from_zero_indexed(0),
1740        };
1741        (loc, loc)
1742    };
1743    if linetable.is_empty() {
1744        return alloc::vec![default_loc(); num_instructions].into_boxed_slice();
1745    }
1746
1747    let mut locations = Vec::with_capacity(num_instructions);
1748    let mut pos = 0;
1749    let mut line = first_line;
1750
1751    while pos < linetable.len() && locations.len() < num_instructions {
1752        let first_byte = linetable[pos];
1753        pos += 1;
1754        if first_byte & 0x80 == 0 {
1755            break;
1756        }
1757        let code = (first_byte >> 3) & 0x0f;
1758        let length = ((first_byte & 0x07) + 1) as usize;
1759        let kind = match PyCodeLocationInfoKind::from_code(code) {
1760            Some(k) => k,
1761            None => break,
1762        };
1763
1764        let (line_delta, end_line_delta, col, end_col): (i32, i32, Option<u32>, Option<u32>) =
1765            match kind {
1766                PyCodeLocationInfoKind::None => (0, 0, None, None),
1767                PyCodeLocationInfoKind::Long => {
1768                    let d = lt_read_signed_varint(linetable, &mut pos);
1769                    let ed = lt_read_varint(linetable, &mut pos) as i32;
1770                    let c = lt_read_varint(linetable, &mut pos);
1771                    let ec = lt_read_varint(linetable, &mut pos);
1772                    (
1773                        d,
1774                        ed,
1775                        if c == 0 { None } else { Some(c - 1) },
1776                        if ec == 0 { None } else { Some(ec - 1) },
1777                    )
1778                }
1779                PyCodeLocationInfoKind::NoColumns => {
1780                    (lt_read_signed_varint(linetable, &mut pos), 0, None, None)
1781                }
1782                PyCodeLocationInfoKind::OneLine0
1783                | PyCodeLocationInfoKind::OneLine1
1784                | PyCodeLocationInfoKind::OneLine2 => {
1785                    let c = lt_byte(linetable, &mut pos) as u32;
1786                    let ec = lt_byte(linetable, &mut pos) as u32;
1787                    (kind.one_line_delta().unwrap_or(0), 0, Some(c), Some(ec))
1788                }
1789                _ if kind.is_short() => {
1790                    let d = lt_byte(linetable, &mut pos);
1791                    let g = kind.short_column_group().unwrap_or(0);
1792                    let c = ((g as u32) << 3) | ((d >> 4) as u32);
1793                    (0, 0, Some(c), Some(c + (d & 0x0f) as u32))
1794                }
1795                _ => (0, 0, None, None),
1796            };
1797
1798        line += line_delta;
1799        let mk = |l: i32| {
1800            if l > 0 {
1801                OneIndexed::new(l as usize).unwrap_or(OneIndexed::MIN)
1802            } else {
1803                OneIndexed::MIN
1804            }
1805        };
1806        for _ in 0..length {
1807            if locations.len() >= num_instructions {
1808                break;
1809            }
1810            if kind == PyCodeLocationInfoKind::None {
1811                let loc = SourceLocation {
1812                    line: mk(line),
1813                    character_offset: OneIndexed::from_zero_indexed(0),
1814                };
1815                locations.push((loc, loc));
1816            } else {
1817                locations.push((
1818                    SourceLocation {
1819                        line: mk(line),
1820                        character_offset: OneIndexed::from_zero_indexed(col.unwrap_or(0) as usize),
1821                    },
1822                    SourceLocation {
1823                        line: mk(line + end_line_delta),
1824                        character_offset: OneIndexed::from_zero_indexed(
1825                            end_col.unwrap_or(0) as usize
1826                        ),
1827                    },
1828                ));
1829            }
1830        }
1831    }
1832    while locations.len() < num_instructions {
1833        locations.push(default_loc());
1834    }
1835    locations.into_boxed_slice()
1836}
1837
1838fn lt_byte(data: &[u8], pos: &mut usize) -> u8 {
1839    if *pos < data.len() {
1840        let b = data[*pos];
1841        *pos += 1;
1842        b
1843    } else {
1844        0
1845    }
1846}
1847
1848/// Linetable uses little-endian varint.
1849fn lt_read_varint(data: &[u8], pos: &mut usize) -> u32 {
1850    let mut result: u32 = 0;
1851    let mut shift = 0;
1852    loop {
1853        if *pos >= data.len() {
1854            break;
1855        }
1856        let b = data[*pos];
1857        *pos += 1;
1858        result |= ((b & 0x3f) as u32) << shift;
1859        shift += 6;
1860        if b & 0x40 == 0 {
1861            break;
1862        }
1863    }
1864    result
1865}
1866
1867fn lt_read_signed_varint(data: &[u8], pos: &mut usize) -> i32 {
1868    let val = lt_read_varint(data, pos);
1869    if val & 1 != 0 {
1870        -((val >> 1) as i32)
1871    } else {
1872        (val >> 1) as i32
1873    }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878    use super::*;
1879    use crate::bytecode::{BasicBag, ConstantData};
1880
1881    fn hex_to_bytes(hex: &str) -> Vec<u8> {
1882        (0..hex.len())
1883            .step_by(2)
1884            .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
1885            .collect()
1886    }
1887
1888    fn decode_code(hex: &str) -> CodeObject<ConstantData> {
1889        let bytes = hex_to_bytes(hex);
1890        let value = deserialize_value(&mut &bytes[..], BasicBag).expect("decode failed");
1891        match value {
1892            ConstantData::Code { code } => *code,
1893            other => panic!("expected Code, got {other:?}"),
1894        }
1895    }
1896
1897    fn decode_tuple(hex: &str) -> Vec<ConstantData> {
1898        let bytes = hex_to_bytes(hex);
1899        let value = deserialize_value(&mut &bytes[..], BasicBag).expect("decode failed");
1900        match value {
1901            ConstantData::Tuple { elements } => elements,
1902            other => panic!("expected Tuple, got {other:?}"),
1903        }
1904    }
1905
1906    /// CPython 3.14 marshal output for: `compile("x = 1", "<t>", "exec")`.
1907    /// Exercises FLAG_REF on the code object and TYPE_REF for qualname
1908    /// pointing back at the obj_name slot.
1909    #[test]
1910    fn cpython_314_trivial_assignment() {
1911        let hex = "e30000000000000000000000000100000000000000f30a00000080005e017400520123002902\
1912                   e9010000004e2901da0178a900f300000000da033c743eda083c6d6f64756c653e72070000000100\
1913                   0000730a000000f003010101d8040582017205000000";
1914        let code = decode_code(hex);
1915        assert_eq!(code.obj_name.as_str(), "<module>");
1916        assert_eq!(code.qualname.as_str(), "<module>");
1917        assert_eq!(code.source_path.as_str(), "<t>");
1918        assert_eq!(code.arg_count, 0);
1919        assert_eq!(code.max_stackdepth, 1);
1920        assert_eq!(code.names.len(), 1);
1921        assert_eq!(code.names[0].as_str(), "x");
1922        assert_eq!(code.constants.len(), 2);
1923        // (1, None)
1924        let consts: &[ConstantData] = &code.constants;
1925        assert!(matches!(
1926            consts[0],
1927            ConstantData::Integer { ref value } if *value == 1.into(),
1928        ));
1929        assert!(matches!(consts[1], ConstantData::None));
1930    }
1931
1932    /// CPython 3.14 marshal output for a module with a nested function
1933    /// and a string constant. Verifies that nested code objects inside
1934    /// a const tuple share the surrounding code's ref space.
1935    #[test]
1936    fn cpython_314_nested_code_and_string_const() {
1937        let hex = "e30000000000000000000000000100000000000000f310000000800052001700740052017401\
1938                   520223002903630200000000000000000000000200000003000000f3120000008000570\
1939                   12c0000000000000000000000230029014ea9002902da0161da016273020000002626da033c743e\
1940                   da0361646472070000000200000073090000008000d80b0c8d35804cf300000000da0568656c6c\
1941                   6f4e29027207000000da084752454554494e47720300000072080000007206000000da083c6d6f\
1942                   64756c653e720b000000010000007311000000f003010101f204010111f006000c1382087208000000";
1943        let code = decode_code(hex);
1944        assert_eq!(code.obj_name.as_str(), "<module>");
1945        assert_eq!(code.names.len(), 2);
1946        assert_eq!(code.names[0].as_str(), "add");
1947        assert_eq!(code.names[1].as_str(), "GREETING");
1948        assert_eq!(code.constants.len(), 3);
1949        // Inner code, "hello", None
1950        let consts: &[ConstantData] = &code.constants;
1951        let inner = match &consts[0] {
1952            ConstantData::Code { code } => code,
1953            other => panic!("expected nested Code, got {other:?}"),
1954        };
1955        assert_eq!(inner.obj_name.as_str(), "add");
1956        assert_eq!(inner.qualname.as_str(), "add");
1957        assert_eq!(inner.arg_count, 2);
1958        assert_eq!(inner.varnames.len(), 2);
1959        assert_eq!(inner.varnames[0].as_str(), "a");
1960        assert_eq!(inner.varnames[1].as_str(), "b");
1961        assert!(matches!(
1962            consts[1],
1963            ConstantData::Str { ref value } if value.as_str().ok() == Some("hello"),
1964        ));
1965        assert!(matches!(consts[2], ConstantData::None));
1966    }
1967
1968    /// CPython 3.14 marshal output for:
1969    /// `(compile("x = 1", "<t>", "exec"),)`.
1970    /// The outer tuple occupies ref slot 0 and the code object occupies
1971    /// slot 1, so code-object fields must preserve that global ref offset.
1972    #[test]
1973    fn cpython_314_code_inside_tuple_preserves_ref_indexes() {
1974        let hex = "a901630000000000000000000000000100000000000000f30a00000080005e017400\
1975                   520123002902e9010000004e2901da0178a900f300000000da033c743eda083c6d6f\
1976                   64756c653e720700000001000000730a000000f003010101d8040582017205000000";
1977        let tuple = decode_tuple(hex);
1978        assert_eq!(tuple.len(), 1);
1979        let code = match &tuple[0] {
1980            ConstantData::Code { code } => code,
1981            other => panic!("expected nested Code, got {other:?}"),
1982        };
1983        assert_eq!(code.obj_name.as_str(), "<module>");
1984        assert_eq!(code.qualname.as_str(), "<module>");
1985        assert_eq!(code.source_path.as_str(), "<t>");
1986        assert_eq!(code.names.len(), 1);
1987        assert_eq!(code.names[0].as_str(), "x");
1988        assert_eq!(code.constants.len(), 2);
1989    }
1990}