@@ -397,6 +397,9 @@ pub enum Insn {
397397 GetIvar { self_val : InsnId , id : ID , state : InsnId } ,
398398 /// Set `self_val`'s instance variable `id` to `val`
399399 SetIvar { self_val : InsnId , id : ID , val : InsnId , state : InsnId } ,
400+ /// Check whether an instance variable exists on `self_val`
401+ DefinedIvar { self_val : InsnId , id : ID , pushval : VALUE , state : InsnId } ,
402+
400403
401404 /// Own a FrameState so that instructions can look up their dominating FrameState when
402405 /// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
@@ -603,6 +606,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
603606 Ok ( ( ) )
604607 } ,
605608 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state) ,
609+ Insn :: DefinedIvar { self_val, id, .. } => write ! ( f, "DefinedIvar {self_val}, :{}" , id. contents_lossy( ) . into_owned( ) ) ,
606610 Insn :: GetIvar { self_val, id, .. } => write ! ( f, "GetIvar {self_val}, :{}" , id. contents_lossy( ) . into_owned( ) ) ,
607611 Insn :: SetIvar { self_val, id, val, .. } => write ! ( f, "SetIvar {self_val}, :{}, {val}" , id. contents_lossy( ) . into_owned( ) ) ,
608612 Insn :: ToArray { val, .. } => write ! ( f, "ToArray {val}" ) ,
@@ -951,6 +955,7 @@ impl Function {
951955 & HashDup { val , state } => HashDup { val : find ! ( val) , state } ,
952956 & CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun : cfun, args : args. iter ( ) . map ( |arg| find ! ( * arg) ) . collect ( ) , name : name, return_type : return_type, elidable } ,
953957 & Defined { op_type, obj, pushval, v } => Defined { op_type, obj, pushval, v : find ! ( v) } ,
958+ & DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val : find ! ( self_val) , pushval, id, state } ,
954959 NewArray { elements, state } => NewArray { elements : find_vec ! ( * elements) , state : find ! ( * state) } ,
955960 & NewHash { ref elements, state } => {
956961 let mut found_elements = vec ! [ ] ;
@@ -1039,6 +1044,7 @@ impl Function {
10391044 Insn :: SendWithoutBlockDirect { .. } => types:: BasicObject ,
10401045 Insn :: Send { .. } => types:: BasicObject ,
10411046 Insn :: Defined { .. } => types:: BasicObject ,
1047+ Insn :: DefinedIvar { .. } => types:: BasicObject ,
10421048 Insn :: GetConstantPath { .. } => types:: BasicObject ,
10431049 Insn :: ArrayMax { .. } => types:: BasicObject ,
10441050 Insn :: GetIvar { .. } => types:: BasicObject ,
@@ -1599,7 +1605,7 @@ impl Function {
15991605 worklist. push_back ( state) ;
16001606 }
16011607 Insn :: CCall { args, .. } => worklist. extend ( args) ,
1602- Insn :: GetIvar { self_val, state, .. } => {
1608+ Insn :: GetIvar { self_val, state, .. } | Insn :: DefinedIvar { self_val , state , .. } => {
16031609 worklist. push_back ( self_val) ;
16041610 worklist. push_back ( state) ;
16051611 }
@@ -2165,6 +2171,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
21652171 let v = state. stack_pop ( ) ?;
21662172 state. stack_push ( fun. push_insn ( block, Insn :: Defined { op_type, obj, pushval, v } ) ) ;
21672173 }
2174+ YARVINSN_definedivar => {
2175+ // (ID id, IVC ic, VALUE pushval)
2176+ let id = ID ( get_arg ( pc, 0 ) . as_u64 ( ) ) ;
2177+ let pushval = get_arg ( pc, 2 ) ;
2178+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
2179+ let self_val = fun. push_insn ( block, Insn :: PutSelf ) ;
2180+ state. stack_push ( fun. push_insn ( block, Insn :: DefinedIvar { self_val, id, pushval, state : exit_id } ) ) ;
2181+ }
21682182 YARVINSN_opt_getconstant_path => {
21692183 let ic = get_arg ( pc, 0 ) . as_ptr ( ) ;
21702184 state. stack_push ( fun. push_insn ( block, Insn :: GetConstantPath { ic } ) ) ;
@@ -2991,6 +3005,39 @@ mod tests {
29913005 "# ] ] ) ;
29923006 }
29933007
3008+ #[ test]
3009+ fn defined_ivar ( ) {
3010+ eval ( "
3011+ def test = defined?(@foo)
3012+ " ) ;
3013+ assert_method_hir_with_opcode ( "test" , YARVINSN_definedivar , expect ! [ [ r#"
3014+ fn test:
3015+ bb0():
3016+ v2:BasicObject = PutSelf
3017+ v3:BasicObject = DefinedIvar v2, :@foo
3018+ Return v3
3019+ "# ] ] ) ;
3020+ }
3021+
3022+ #[ test]
3023+ fn defined ( ) {
3024+ eval ( "
3025+ def test = return defined?(SeaChange), defined?(favourite), defined?($ruby)
3026+ " ) ;
3027+ assert_method_hir_with_opcode ( "test" , YARVINSN_defined , expect ! [ [ r#"
3028+ fn test:
3029+ bb0():
3030+ v1:NilClassExact = Const Value(nil)
3031+ v2:BasicObject = Defined constant, v1
3032+ v3:BasicObject = PutSelf
3033+ v4:BasicObject = Defined func, v3
3034+ v5:NilClassExact = Const Value(nil)
3035+ v6:BasicObject = Defined global-variable, v5
3036+ v8:ArrayExact = NewArray v2, v4, v6
3037+ Return v8
3038+ "# ] ] ) ;
3039+ }
3040+
29943041 #[ test]
29953042 fn test_return_const ( ) {
29963043 eval ( "
0 commit comments