@@ -321,6 +321,29 @@ impl From<RangeType> for u32 {
321321 }
322322}
323323
324+ /// Special regex backref symbol types
325+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
326+ pub enum SpecialBackrefSymbol {
327+ LastMatch , // $&
328+ PreMatch , // $`
329+ PostMatch , // $'
330+ LastGroup , // $+
331+ }
332+
333+ impl TryFrom < u8 > for SpecialBackrefSymbol {
334+ type Error = String ;
335+
336+ fn try_from ( value : u8 ) -> Result < Self , Self :: Error > {
337+ match value as char {
338+ '&' => Ok ( SpecialBackrefSymbol :: LastMatch ) ,
339+ '`' => Ok ( SpecialBackrefSymbol :: PreMatch ) ,
340+ '\'' => Ok ( SpecialBackrefSymbol :: PostMatch ) ,
341+ '+' => Ok ( SpecialBackrefSymbol :: LastGroup ) ,
342+ c => Err ( format ! ( "invalid backref symbol: '{}'" , c) ) ,
343+ }
344+ }
345+ }
346+
324347/// Print adaptor for [`Const`]. See [`PtrPrintMap`].
325348struct ConstPrinter < ' a > {
326349 inner : & ' a Const ,
@@ -415,6 +438,7 @@ pub enum SideExitReason {
415438 PatchPoint ( Invariant ) ,
416439 CalleeSideExit ,
417440 ObjToStringFallback ,
441+ UnknownSpecialVariable ( u64 ) ,
418442}
419443
420444impl std:: fmt:: Display for SideExitReason {
@@ -494,6 +518,8 @@ pub enum Insn {
494518 GetLocal { level : u32 , ep_offset : u32 } ,
495519 /// Set a local variable in a higher scope or the heap
496520 SetLocal { level : u32 , ep_offset : u32 , val : InsnId } ,
521+ GetSpecialSymbol { symbol_type : SpecialBackrefSymbol , state : InsnId } ,
522+ GetSpecialNumber { nth : u64 , state : InsnId } ,
497523
498524 /// Own a FrameState so that instructions can look up their dominating FrameState when
499525 /// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
@@ -774,6 +800,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
774800 Insn :: SetGlobal { id, val, .. } => write ! ( f, "SetGlobal :{}, {val}" , id. contents_lossy( ) ) ,
775801 Insn :: GetLocal { level, ep_offset } => write ! ( f, "GetLocal l{level}, EP@{ep_offset}" ) ,
776802 Insn :: SetLocal { val, level, ep_offset } => write ! ( f, "SetLocal l{level}, EP@{ep_offset}, {val}" ) ,
803+ Insn :: GetSpecialSymbol { symbol_type, .. } => write ! ( f, "GetSpecialSymbol {symbol_type:?}" ) ,
804+ Insn :: GetSpecialNumber { nth, .. } => write ! ( f, "GetSpecialNumber {nth}" ) ,
777805 Insn :: ToArray { val, .. } => write ! ( f, "ToArray {val}" ) ,
778806 Insn :: ToNewArray { val, .. } => write ! ( f, "ToNewArray {val}" ) ,
779807 Insn :: ArrayExtend { left, right, .. } => write ! ( f, "ArrayExtend {left}, {right}" ) ,
@@ -1221,6 +1249,8 @@ impl Function {
12211249 & GetIvar { self_val, id, state } => GetIvar { self_val : find ! ( self_val) , id, state } ,
12221250 & SetIvar { self_val, id, val, state } => SetIvar { self_val : find ! ( self_val) , id, val : find ! ( val) , state } ,
12231251 & SetLocal { val, ep_offset, level } => SetLocal { val : find ! ( val) , ep_offset, level } ,
1252+ & GetSpecialSymbol { symbol_type, state } => GetSpecialSymbol { symbol_type, state } ,
1253+ & GetSpecialNumber { nth, state } => GetSpecialNumber { nth, state } ,
12241254 & ToArray { val, state } => ToArray { val : find ! ( val) , state } ,
12251255 & ToNewArray { val, state } => ToNewArray { val : find ! ( val) , state } ,
12261256 & ArrayExtend { left, right, state } => ArrayExtend { left : find ! ( left) , right : find ! ( right) , state } ,
@@ -1306,6 +1336,8 @@ impl Function {
13061336 Insn :: ArrayMax { .. } => types:: BasicObject ,
13071337 Insn :: GetGlobal { .. } => types:: BasicObject ,
13081338 Insn :: GetIvar { .. } => types:: BasicObject ,
1339+ Insn :: GetSpecialSymbol { .. } => types:: BasicObject ,
1340+ Insn :: GetSpecialNumber { .. } => types:: BasicObject ,
13091341 Insn :: ToNewArray { .. } => types:: ArrayExact ,
13101342 Insn :: ToArray { .. } => types:: ArrayExact ,
13111343 Insn :: ObjToString { .. } => types:: BasicObject ,
@@ -1995,6 +2027,8 @@ impl Function {
19952027 worklist. push_back ( state) ;
19962028 }
19972029 & Insn :: GetGlobal { state, .. } |
2030+ & Insn :: GetSpecialSymbol { state, .. } |
2031+ & Insn :: GetSpecialNumber { state, .. } |
19982032 & Insn :: SideExit { state, .. } => worklist. push_back ( state) ,
19992033 }
20002034 }
@@ -3325,6 +3359,29 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
33253359 let anytostring = fun. push_insn ( block, Insn :: AnyToString { val, str, state : exit_id } ) ;
33263360 state. stack_push ( anytostring) ;
33273361 }
3362+ YARVINSN_getspecial => {
3363+ let key = get_arg ( pc, 0 ) . as_u64 ( ) ;
3364+ let svar = get_arg ( pc, 1 ) . as_u64 ( ) ;
3365+
3366+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
3367+
3368+ if svar == 0 {
3369+ // TODO: Handle non-backref
3370+ fun. push_insn ( block, Insn :: SideExit { state : exit_id, reason : SideExitReason :: UnknownSpecialVariable ( key) } ) ;
3371+ // End the block
3372+ break ;
3373+ } else if svar & 0x01 != 0 {
3374+ // Handle symbol backrefs like $&, $`, $', $+
3375+ let shifted_svar: u8 = ( svar >> 1 ) . try_into ( ) . unwrap ( ) ;
3376+ let symbol_type = SpecialBackrefSymbol :: try_from ( shifted_svar) . expect ( "invalid backref symbol" ) ;
3377+ let result = fun. push_insn ( block, Insn :: GetSpecialSymbol { symbol_type, state : exit_id } ) ;
3378+ state. stack_push ( result) ;
3379+ } else {
3380+ // Handle number backrefs like $1, $2, $3
3381+ let result = fun. push_insn ( block, Insn :: GetSpecialNumber { nth : svar, state : exit_id } ) ;
3382+ state. stack_push ( result) ;
3383+ }
3384+ }
33283385 _ => {
33293386 // Unknown opcode; side-exit into the interpreter
33303387 let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
0 commit comments