mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
kernel.c: regression on struct/array/hash == override with super; fix #6660
when overriding struct#==, array#==, or hash#== with super, the recursion
detection incorrectly treated the super call as a circular reference. this
was caused by commit 5ca2d442 which added recursion detection.
the fix introduces mrb_recursive_func_p that starts from ci[-2] instead of
ci[-1], skipping the immediate parent frame which may be a ruby override
calling super. equality methods (==, eql?) now use this function, while
inspect methods keep using mrb_recursive_method_p for immediate circular
reference detection.
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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))
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user