@@ -93,6 +93,7 @@ use std::ffi::{c_void, CString, CStr};
9393use std:: fmt:: { Debug , Display , Formatter } ;
9494use std:: os:: raw:: { c_char, c_int, c_long, c_uint} ;
9595use std:: panic:: { catch_unwind, UnwindSafe } ;
96+ use std:: ptr:: NonNull ;
9697
9798use crate :: cast:: IntoUsize as _;
9899
@@ -749,9 +750,42 @@ impl VALUE {
749750
750751pub type IseqParameters = rb_iseq_constant_body_rb_iseq_parameters ;
751752
753+ /// How a block iseq refers to a variable in an enclosing scope, as recorded in
754+ /// `ISEQ_BODY(blockiseq)->outer_variables`. `compile.c` aggregates accesses from
755+ /// nested blocks up the chain, and the same table backs `Ractor.shareable_proc`'s
756+ /// isolation checks.
757+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
758+ pub enum OuterLocalAccess {
759+ /// The variable is read but never assigned to.
760+ ReadOnly ,
761+ /// The variable is assigned to and maybe also read.
762+ ReadWrite ,
763+ }
764+
765+ /// Wrapper over an iseq's `outer_variables` table, which describes
766+ /// how a block iseq refers to a variable in an enclosing scope.
767+ #[ derive( Clone , Copy ) ]
768+ pub struct OuterVariables ( Option < NonNull < rb_id_table > > ) ;
769+
770+ impl OuterVariables {
771+ /// Look up how the enclosing-scope local `id` is accessed by the iseq (or any
772+ /// iseq nested within it). Returns `None` when the variable isn't referenced.
773+ pub fn local_access ( self , id : ID ) -> Option < OuterLocalAccess > {
774+ let table = self . 0 ?;
775+ let mut write = Qfalse ;
776+ // Non-zero return means there's a table entry, i.e. the variable is referenced.
777+ if unsafe { rb_id_table_lookup ( table. as_ptr ( ) , id, & mut write) } == 0 {
778+ return None ;
779+ }
780+ // Truthy means write
781+ Some ( if write. test ( ) { OuterLocalAccess :: ReadWrite } else { OuterLocalAccess :: ReadOnly } )
782+ }
783+ }
784+
752785/// Extension trait to enable method calls on [`IseqPtr`]
753786pub trait IseqAccess {
754787 unsafe fn params < ' a > ( self ) -> & ' a IseqParameters ;
788+ unsafe fn outer_variables ( self ) -> OuterVariables ;
755789}
756790
757791impl IseqAccess for IseqPtr {
@@ -760,6 +794,13 @@ impl IseqAccess for IseqPtr {
760794 use crate :: cast:: IntoUsize ;
761795 unsafe { & * ( ( * self ) . body . byte_add ( ISEQ_BODY_OFFSET_PARAM . to_usize ( ) ) as * const IseqParameters ) }
762796 }
797+
798+ /// The iseq's `outer_variables` table. See [`OuterVariables`].
799+ unsafe fn outer_variables ( self ) -> OuterVariables {
800+ use crate :: cast:: IntoUsize ;
801+ let field = unsafe { ( * self ) . body . byte_add ( ISEQ_BODY_OFFSET_OUTER_VARIABLES . to_usize ( ) ) } as * const * mut rb_id_table ;
802+ OuterVariables ( NonNull :: new ( unsafe { * field } ) )
803+ }
763804}
764805
765806impl IseqParameters {
0 commit comments