@@ -555,6 +555,18 @@ pub trait MarshalBag: Copy {
555555 code : CodeObject < <Self :: ConstantBag as ConstantBag >:: Constant > ,
556556 ) -> Self :: Value ;
557557
558+ /// Construct a runtime code object while retaining the exact values read
559+ /// from ``co_consts``. Compiler bags ignore this second channel; runtime
560+ /// bags use it for marshalable values (lists, dicts, sets, recursive
561+ /// containers) that their compiler constant representation cannot hold.
562+ fn make_code_with_constants (
563+ & self ,
564+ code : CodeObject < <Self :: ConstantBag as ConstantBag >:: Constant > ,
565+ _constants : Vec < Self :: Value > ,
566+ ) -> Self :: Value {
567+ self . make_code ( code)
568+ }
569+
558570 fn make_stop_iter ( & self ) -> Result < Self :: Value > ;
559571
560572 fn make_list ( & self , it : impl Iterator < Item = Self :: Value > ) -> Result < Self :: Value > ;
@@ -630,6 +642,30 @@ pub trait MarshalBag: Copy {
630642 ) -> Option < <Self :: ConstantBag as ConstantBag >:: Constant > {
631643 None
632644 }
645+
646+ /// Convert a runtime constant to the compiler-side shape stored in
647+ /// ``CodeObject``. Runtime implementations may return a semantically
648+ /// unused placeholder when the exact value is carried by
649+ /// `make_code_with_constants` instead.
650+ fn code_constant_from_value (
651+ & self ,
652+ value : & Self :: Value ,
653+ ) -> Result < <Self :: ConstantBag as ConstantBag >:: Constant > {
654+ self . constant_ref_from_value ( value)
655+ . ok_or ( MarshalError :: BadType )
656+ }
657+
658+ fn bytes_from_value ( & self , _value : & Self :: Value ) -> Option < Vec < u8 > > {
659+ None
660+ }
661+
662+ fn str_from_value ( & self , _value : & Self :: Value ) -> Option < alloc:: string:: String > {
663+ None
664+ }
665+
666+ fn tuple_elements_from_value ( & self , _value : & Self :: Value ) -> Option < Vec < Self :: Value > > {
667+ None
668+ }
633669}
634670
635671impl < Bag : ConstantBag > MarshalBag for Bag {
@@ -731,6 +767,27 @@ impl<Bag: ConstantBag> MarshalBag for Bag {
731767 ) -> Option < <Self :: ConstantBag as ConstantBag >:: Constant > {
732768 Some ( value. clone ( ) )
733769 }
770+
771+ fn bytes_from_value ( & self , value : & Self :: Value ) -> Option < Vec < u8 > > {
772+ match value. borrow_constant ( ) {
773+ BorrowedConstant :: Bytes { value } => Some ( value. to_vec ( ) ) ,
774+ _ => None ,
775+ }
776+ }
777+
778+ fn str_from_value ( & self , value : & Self :: Value ) -> Option < alloc:: string:: String > {
779+ match value. borrow_constant ( ) {
780+ BorrowedConstant :: Str { value } => Some ( value. to_string_lossy ( ) . into_owned ( ) ) ,
781+ _ => None ,
782+ }
783+ }
784+
785+ fn tuple_elements_from_value ( & self , value : & Self :: Value ) -> Option < Vec < Self :: Value > > {
786+ match value. borrow_constant ( ) {
787+ BorrowedConstant :: Tuple { elements } => Some ( elements. to_vec ( ) ) ,
788+ _ => None ,
789+ }
790+ }
734791}
735792
736793pub const MAX_MARSHAL_STACK_DEPTH : usize = 2000 ;
@@ -789,20 +846,8 @@ fn deserialize_value_after_header<R: Read, Bag: MarshalBag>(
789846 } ;
790847
791848 let typ = Type :: try_from ( type_code) ?;
792- // CPython's r_object() uses one global ref table: TYPE_CODE reserves its
793- // slot before reading code fields, and those fields may use later TYPE_REF
794- // indexes. Keep the same indexes even when Bag::Value and Constant differ.
795849 let value = if matches ! ( typ, Type :: Code ) {
796- let mut inner_refs: Vec < Option < <Bag :: ConstantBag as ConstantBag >:: Constant > > = refs
797- . iter ( )
798- . map ( |value| {
799- value
800- . as_ref ( )
801- . and_then ( |value| bag. constant_ref_from_value ( value) )
802- } )
803- . collect ( ) ;
804- let code = deserialize_code_inner ( rdr, bag. constant_bag ( ) , depth - 1 , & mut inner_refs) ?;
805- bag. make_code ( code)
850+ deserialize_code_value_inner ( rdr, bag, depth - 1 , refs) ?
806851 } else {
807852 deserialize_value_typed ( rdr, bag, depth, refs, typ, slot) ?
808853 } ;
@@ -813,6 +858,137 @@ fn deserialize_value_after_header<R: Read, Bag: MarshalBag>(
813858 Ok ( value)
814859}
815860
861+ /// Decode a code object through the runtime bag. CPython's marshal reader
862+ /// keeps one reference table for the code fields and `co_consts`; using
863+ /// `Bag::Value` here preserves that index space and lets runtime-only
864+ /// constants survive alongside the compiler representation.
865+ fn deserialize_code_value_inner < R : Read , Bag : MarshalBag > (
866+ rdr : & mut R ,
867+ bag : Bag ,
868+ depth : usize ,
869+ refs : & mut Vec < Option < Bag :: Value > > ,
870+ ) -> Result < Bag :: Value > {
871+ if depth == 0 {
872+ return Err ( MarshalError :: InvalidBytecode ) ;
873+ }
874+ let arg_count = rdr. read_u32 ( ) ?;
875+ let posonlyarg_count = rdr. read_u32 ( ) ?;
876+ let kwonlyarg_count = rdr. read_u32 ( ) ?;
877+ let max_stackdepth = rdr. read_u32 ( ) ?;
878+ let flags = CodeFlags :: from_bits_truncate ( rdr. read_u32 ( ) ?) ;
879+ let child_depth = depth - 1 ;
880+
881+ let code_value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
882+ let code_bytes = bag
883+ . bytes_from_value ( & code_value)
884+ . ok_or ( MarshalError :: BadType ) ?;
885+
886+ let consts_value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
887+ let constant_values = bag
888+ . tuple_elements_from_value ( & consts_value)
889+ . ok_or ( MarshalError :: BadType ) ?;
890+ let constants = constant_values
891+ . iter ( )
892+ . map ( |value| bag. code_constant_from_value ( value) )
893+ . collect :: < Result < Vec < _ > > > ( ) ?
894+ . into_iter ( )
895+ . collect ( ) ;
896+
897+ let read_strings =
898+ |rdr : & mut R , refs : & mut Vec < Option < Bag :: Value > > | -> Result < Vec < alloc:: string:: String > > {
899+ let tuple = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
900+ bag. tuple_elements_from_value ( & tuple)
901+ . ok_or ( MarshalError :: BadType ) ?
902+ . iter ( )
903+ . map ( |value| bag. str_from_value ( value) . ok_or ( MarshalError :: BadType ) )
904+ . collect ( )
905+ } ;
906+ let names_raw = read_strings ( rdr, refs) ?;
907+ let localsplusnames = read_strings ( rdr, refs) ?;
908+
909+ let kinds_value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
910+ let localspluskinds = bag
911+ . bytes_from_value ( & kinds_value)
912+ . ok_or ( MarshalError :: BadType ) ?;
913+
914+ let read_string =
915+ |rdr : & mut R , refs : & mut Vec < Option < Bag :: Value > > | -> Result < alloc:: string:: String > {
916+ let value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
917+ bag. str_from_value ( & value) . ok_or ( MarshalError :: BadType )
918+ } ;
919+ let source_path_raw = read_string ( rdr, refs) ?;
920+ let obj_name_raw = read_string ( rdr, refs) ?;
921+ let qualname_raw = read_string ( rdr, refs) ?;
922+
923+ let first_line_raw = rdr. read_u32 ( ) ? as i32 ;
924+ let first_line_number = if first_line_raw > 0 {
925+ OneIndexed :: new ( first_line_raw as usize )
926+ } else {
927+ None
928+ } ;
929+ let linetable_value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
930+ let linetable = bag
931+ . bytes_from_value ( & linetable_value)
932+ . ok_or ( MarshalError :: BadType ) ?
933+ . into_boxed_slice ( ) ;
934+ let exceptiontable_value = deserialize_value_depth ( rdr, bag, child_depth, refs) ?;
935+ let exceptiontable = bag
936+ . bytes_from_value ( & exceptiontable_value)
937+ . ok_or ( MarshalError :: BadType ) ?
938+ . into_boxed_slice ( ) ;
939+
940+ let lp = split_localplus (
941+ & localsplusnames
942+ . iter ( )
943+ . map ( |s| s. as_str ( ) )
944+ . collect :: < Vec < _ > > ( ) ,
945+ & localspluskinds,
946+ arg_count,
947+ kwonlyarg_count,
948+ flags,
949+ ) ?;
950+ let instructions = CodeUnits :: try_from ( code_bytes. as_slice ( ) ) ?;
951+ let locations = linetable_to_locations ( & linetable, first_line_raw, instructions. len ( ) ) ;
952+ let constant_bag = bag. constant_bag ( ) ;
953+ let code = CodeObject {
954+ instructions,
955+ locations,
956+ flags,
957+ posonlyarg_count,
958+ arg_count,
959+ kwonlyarg_count,
960+ source_path : constant_bag. make_name ( & source_path_raw) ,
961+ first_line_number,
962+ max_stackdepth,
963+ obj_name : constant_bag. make_name ( & obj_name_raw) ,
964+ qualname : constant_bag. make_name ( & qualname_raw) ,
965+ constants,
966+ names : names_raw
967+ . iter ( )
968+ . map ( |name| constant_bag. make_name ( name) )
969+ . collect ( ) ,
970+ varnames : lp
971+ . varnames
972+ . iter ( )
973+ . map ( |name| constant_bag. make_name ( name) )
974+ . collect ( ) ,
975+ cellvars : lp
976+ . cellvars
977+ . iter ( )
978+ . map ( |name| constant_bag. make_name ( name) )
979+ . collect ( ) ,
980+ freevars : lp
981+ . freevars
982+ . iter ( )
983+ . map ( |name| constant_bag. make_name ( name) )
984+ . collect ( ) ,
985+ localspluskinds : localspluskinds. into_boxed_slice ( ) ,
986+ linetable,
987+ exceptiontable,
988+ } ;
989+ Ok ( bag. make_code_with_constants ( code, constant_values) )
990+ }
991+
816992fn deserialize_value_typed < R : Read , Bag : MarshalBag > (
817993 rdr : & mut R ,
818994 bag : Bag ,
0 commit comments