diff --git a/include/mruby.h b/include/mruby.h index 76aa962da..6331ca8c9 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -1327,6 +1327,7 @@ MRB_API mrb_int mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2); /* recursion detection */ MRB_API mrb_bool mrb_recursive_method_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value obj2); +MRB_API mrb_bool mrb_recursive_func_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value obj2); #define MRB_RECURSIVE_P(mrb, mid, obj1, obj2) \ mrb_recursive_method_p(mrb, mid, obj1, obj2) @@ -1337,6 +1338,12 @@ MRB_API mrb_bool mrb_recursive_method_p(mrb_state *mrb, mrb_sym mid, mrb_value o #define MRB_RECURSIVE_BINARY_P(mrb, mid, obj1, obj2) \ mrb_recursive_method_p(mrb, mid, obj1, obj2) +#define MRB_RECURSIVE_FUNC_P(mrb, mid, obj) \ + mrb_recursive_func_p(mrb, mid, obj, mrb_nil_value()) + +#define MRB_RECURSIVE_BINARY_FUNC_P(mrb, mid, obj1, obj2) \ + mrb_recursive_func_p(mrb, mid, obj1, obj2) + #define mrb_gc_arena_save(mrb) ((mrb)->gc.arena_idx) #define mrb_gc_arena_restore(mrb, idx) ((mrb)->gc.arena_idx = (idx)) diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 1e04998fd..c4af92c2f 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -605,7 +605,7 @@ mrb_struct_equal(mrb_state *mrb, mrb_value s) } /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_OPSYM(eq), s, s2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_OPSYM(eq), s, s2)) { return mrb_false_value(); } @@ -649,7 +649,7 @@ mrb_struct_eql(mrb_state *mrb, mrb_value s) } /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_SYM_Q(eql), s, s2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_SYM_Q(eql), s, s2)) { return mrb_false_value(); } diff --git a/src/array.c b/src/array.c index c14534b59..2a0412119 100644 --- a/src/array.c +++ b/src/array.c @@ -1890,7 +1890,7 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1) if (n == 0) return mrb_false_value(); /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_OPSYM(eq), ary1, ary2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_OPSYM(eq), ary1, ary2)) { return mrb_false_value(); } @@ -1921,7 +1921,7 @@ mrb_ary_eql(mrb_state *mrb, mrb_value ary1) if (n == 0) return mrb_false_value(); /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_SYM_Q(eql), ary1, ary2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_SYM_Q(eql), ary1, ary2)) { return mrb_false_value(); } diff --git a/src/hash.c b/src/hash.c index 907db0c2a..f39577af7 100644 --- a/src/hash.c +++ b/src/hash.c @@ -2163,7 +2163,7 @@ mrb_hash_equal(mrb_state *mrb, mrb_value hash) } /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_OPSYM(eq), hash, hash2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_OPSYM(eq), hash, hash2)) { return mrb_false_value(); } @@ -2211,7 +2211,7 @@ mrb_hash_eql(mrb_state *mrb, mrb_value hash) } /* Check for recursion */ - if (MRB_RECURSIVE_BINARY_P(mrb, MRB_SYM_Q(eql), hash, hash2)) { + if (MRB_RECURSIVE_BINARY_FUNC_P(mrb, MRB_SYM_Q(eql), hash, hash2)) { return mrb_false_value(); } diff --git a/src/kernel.c b/src/kernel.c index a78dc6dc6..2cb055985 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -167,6 +167,30 @@ mrb_recursive_method_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value ob return FALSE; } +/** + * Check if a C function call is recursive. + * + * Like mrb_recursive_method_p, but starts from ci[-2] to skip the immediate + * parent frame. Use this from C functions implementing Ruby methods that can + * be overridden with super calls. + */ +MRB_API mrb_bool +mrb_recursive_func_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value obj2) +{ + /* Start from ci[-2] to skip immediate parent frame which may be a + Ruby override calling super */ + for (mrb_callinfo *ci=&mrb->c->ci[-2]; ci>=mrb->c->cibase; ci--) { + if (ci->mid == mid && mrb_obj_eq(mrb, obj1, ci->stack[0])) { + /* For unary methods, only check first argument */ + if (mrb_nil_p(obj2)) return TRUE; + + /* For binary methods, check both arguments */ + if (mrb_obj_eq(mrb, obj2, ci->stack[1])) return TRUE; + } + } + return FALSE; +} + static mrb_value mrb_obj_method_recursive_p(mrb_state *mrb, mrb_value obj) {