From d98d62ee355c4ef71936596f56b9fa9830621ea6 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 17 Feb 2026 10:37:09 +0900 Subject: [PATCH] hash.c: change __except to take array argument mrb_get_args(mrb, "*", ...) internally allocates an array when arguments are on the stack, so passing keys as direct arguments did not actually avoid allocation. Change __except to take a single array argument instead, which is simpler and GC-safe. Co-authored-by: Claude --- mrbgems/mruby-compiler/core/codegen.c | 20 ++++++-------------- src/hash.c | 17 +++++++++-------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index d2c5b1ce8..b4acfbb80 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -5026,7 +5026,7 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos, pop(); } else if (pat_hash->rest && pat_hash->rest != (node*)-2) { - /* **var: capture remaining keys via hash.__except(key1, key2, ...) */ + /* **var: capture remaining keys via hash.__except(keys_array) */ struct mrb_ast_pat_var_node *rest_var = pat_var_node(pat_hash->rest); if (rest_var->name) { int var_idx = lv_idx(s, rest_var->name); @@ -5034,7 +5034,7 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos, gen_move(s, recv, hash_reg, 0); push(); if (num_keys > 0) { - /* Pass matched keys as arguments */ + /* Build array of matched keys */ int i = 0; for (pair = pat_hash->pairs; pair; pair = pair->cdr, i++) { node *key = pair->car->car; @@ -5046,18 +5046,10 @@ codegen_pattern(codegen_scope *s, node *pattern, int target, uint32_t *fail_pos, } push(); } - if (num_keys < CALL_MAXARGS) { - /* Direct arguments */ - genop_3(s, OP_SEND, recv, sym_idx(s, MRB_SYM_2(s->mrb, __except)), num_keys); - for (i = 0; i < num_keys; i++) pop(); - } - else { - /* Too many keys: pack into array */ - genop_2(s, OP_ARRAY, recv + 1, num_keys); - for (i = 1; i < num_keys; i++) pop(); - genop_3(s, OP_SEND, recv, sym_idx(s, MRB_SYM_2(s->mrb, __except)), CALL_MAXARGS); - pop(); - } + genop_2(s, OP_ARRAY, recv + 1, num_keys); + for (i = 1; i < num_keys; i++) pop(); + genop_3(s, OP_SEND, recv, sym_idx(s, MRB_SYM_2(s->mrb, __except)), 1); + pop(); } else { /* No keys to exclude: rest = hash.dup */ diff --git a/src/hash.c b/src/hash.c index 512ecdf6a..b29244094 100644 --- a/src/hash.c +++ b/src/hash.c @@ -2044,25 +2044,26 @@ mrb_hash_compact(mrb_state *mrb, mrb_value hash) /* * Internal method for pattern matching **rest. - * Returns a new hash excluding specified keys. + * Returns a new hash excluding keys in the given array. * - * {a: 1, b: 2, c: 3}.__except(:a, :c) #=> {b: 2} + * {a: 1, b: 2, c: 3}.__except([:a, :c]) #=> {b: 2} */ static mrb_value mrb_hash_except_keys(mrb_state *mrb, mrb_value hash) { - const mrb_value *argv; - mrb_int argc; - mrb_get_args(mrb, "*", &argv, &argc); + mrb_value keys; + mrb_get_args(mrb, "A", &keys); + const mrb_value *ary = RARRAY_PTR(keys); + mrb_int klen = RARRAY_LEN(keys); mrb_value result = mrb_hash_new(mrb); struct RHash *h = mrb_hash_ptr(hash); int ai = mrb_gc_arena_save(mrb); H_EACH(h, entry) { mrb_bool found = FALSE; - for (mrb_int i = 0; i < argc; i++) { - if (mrb_equal(mrb, entry->key, argv[i])) { + for (mrb_int i = 0; i < klen; i++) { + if (mrb_equal(mrb, entry->key, ary[i])) { found = TRUE; break; } @@ -2313,6 +2314,6 @@ mrb_init_hash(mrb_state *mrb) mrb_define_method_id(mrb, h, MRB_SYM(rassoc), mrb_hash_rassoc, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__merge), mrb_hash_merge_m, MRB_ARGS_REQ(1)); mrb_define_method_id(mrb, h, MRB_SYM(__compact), mrb_hash_compact, MRB_ARGS_NONE()); /* implementation of Hash#compact! */ - mrb_define_method_id(mrb, h, MRB_SYM(__except), mrb_hash_except_keys, MRB_ARGS_ANY()); /* for pattern matching **rest */ + mrb_define_method_id(mrb, h, MRB_SYM(__except), mrb_hash_except_keys, MRB_ARGS_REQ(1)); /* for pattern matching **rest */ } #undef lesser