@@ -321,6 +321,29 @@ impl From<RangeType> for u32 {
321
321
}
322
322
}
323
323
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
+
324
347
/// Print adaptor for [`Const`]. See [`PtrPrintMap`].
325
348
struct ConstPrinter < ' a > {
326
349
inner : & ' a Const ,
@@ -415,6 +438,7 @@ pub enum SideExitReason {
415
438
PatchPoint ( Invariant ) ,
416
439
CalleeSideExit ,
417
440
ObjToStringFallback ,
441
+ UnknownSpecialVariable ( u64 ) ,
418
442
}
419
443
420
444
impl std:: fmt:: Display for SideExitReason {
@@ -494,6 +518,8 @@ pub enum Insn {
494
518
GetLocal { level : u32 , ep_offset : u32 } ,
495
519
/// Set a local variable in a higher scope or the heap
496
520
SetLocal { level : u32 , ep_offset : u32 , val : InsnId } ,
521
+ GetSpecialSymbol { symbol_type : SpecialBackrefSymbol , state : InsnId } ,
522
+ GetSpecialNumber { nth : u64 , state : InsnId } ,
497
523
498
524
/// Own a FrameState so that instructions can look up their dominating FrameState when
499
525
/// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
@@ -774,6 +800,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
774
800
Insn :: SetGlobal { id, val, .. } => write ! ( f, "SetGlobal :{}, {val}" , id. contents_lossy( ) ) ,
775
801
Insn :: GetLocal { level, ep_offset } => write ! ( f, "GetLocal l{level}, EP@{ep_offset}" ) ,
776
802
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}" ) ,
777
805
Insn :: ToArray { val, .. } => write ! ( f, "ToArray {val}" ) ,
778
806
Insn :: ToNewArray { val, .. } => write ! ( f, "ToNewArray {val}" ) ,
779
807
Insn :: ArrayExtend { left, right, .. } => write ! ( f, "ArrayExtend {left}, {right}" ) ,
@@ -1221,6 +1249,8 @@ impl Function {
1221
1249
& GetIvar { self_val, id, state } => GetIvar { self_val : find ! ( self_val) , id, state } ,
1222
1250
& SetIvar { self_val, id, val, state } => SetIvar { self_val : find ! ( self_val) , id, val : find ! ( val) , state } ,
1223
1251
& 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 } ,
1224
1254
& ToArray { val, state } => ToArray { val : find ! ( val) , state } ,
1225
1255
& ToNewArray { val, state } => ToNewArray { val : find ! ( val) , state } ,
1226
1256
& ArrayExtend { left, right, state } => ArrayExtend { left : find ! ( left) , right : find ! ( right) , state } ,
@@ -1306,6 +1336,8 @@ impl Function {
1306
1336
Insn :: ArrayMax { .. } => types:: BasicObject ,
1307
1337
Insn :: GetGlobal { .. } => types:: BasicObject ,
1308
1338
Insn :: GetIvar { .. } => types:: BasicObject ,
1339
+ Insn :: GetSpecialSymbol { .. } => types:: BasicObject ,
1340
+ Insn :: GetSpecialNumber { .. } => types:: BasicObject ,
1309
1341
Insn :: ToNewArray { .. } => types:: ArrayExact ,
1310
1342
Insn :: ToArray { .. } => types:: ArrayExact ,
1311
1343
Insn :: ObjToString { .. } => types:: BasicObject ,
@@ -1995,6 +2027,8 @@ impl Function {
1995
2027
worklist. push_back ( state) ;
1996
2028
}
1997
2029
& Insn :: GetGlobal { state, .. } |
2030
+ & Insn :: GetSpecialSymbol { state, .. } |
2031
+ & Insn :: GetSpecialNumber { state, .. } |
1998
2032
& Insn :: SideExit { state, .. } => worklist. push_back ( state) ,
1999
2033
}
2000
2034
}
@@ -3325,6 +3359,29 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
3325
3359
let anytostring = fun. push_insn ( block, Insn :: AnyToString { val, str, state : exit_id } ) ;
3326
3360
state. stack_push ( anytostring) ;
3327
3361
}
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
+ }
3328
3385
_ => {
3329
3386
// Unknown opcode; side-exit into the interpreter
3330
3387
let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
0 commit comments