From 468184a996c99d1f94f94d7468f65e386cf13564 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sun, 6 Oct 2019 21:18:20 -0700 Subject: Allow ruby2_keywords to be used with bmethods There are libraries that use define_method with argument splats where they would like to pass keywords through the method. To more easily allow such libraries to use ruby2_keywords to handle backwards compatibility, it is necessary for ruby2_keywords to support bmethods. --- vm_method.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'vm_method.c') diff --git a/vm_method.c b/vm_method.c index 554d209110..6465798d30 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1805,15 +1805,43 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module) } if (module == defined_class || origin_class == defined_class) { - if (me->def->type == VM_METHOD_TYPE_ISEQ && - me->def->body.iseq.iseqptr->body->param.flags.has_rest && - !me->def->body.iseq.iseqptr->body->param.flags.has_kw && - !me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) { - me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1; - rb_clear_method_cache_by_class(module); - } - else { - rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby, method accepts keywords, or method does not accept argument splat)", rb_id2name(name)); + switch (me->def->type) { + case VM_METHOD_TYPE_ISEQ: + if (me->def->body.iseq.iseqptr->body->param.flags.has_rest && + !me->def->body.iseq.iseqptr->body->param.flags.has_kw && + !me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) { + me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1; + rb_clear_method_cache_by_class(module); + } + else { + rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name)); + } + break; + case VM_METHOD_TYPE_BMETHOD: { + VALUE procval = me->def->body.bmethod.proc; + if (vm_block_handler_type(procval) == block_handler_type_proc) { + procval = vm_proc_to_block_handler(VM_BH_TO_PROC(procval)); + } + + if (vm_block_handler_type(procval) == block_handler_type_iseq) { + const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(procval); + const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq); + if (iseq->body->param.flags.has_rest && + !iseq->body->param.flags.has_kw && + !iseq->body->param.flags.has_kwrest) { + iseq->body->param.flags.ruby2_keywords = 1; + rb_clear_method_cache_by_class(module); + } + else { + rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name)); + } + return Qnil; + } + } + /* fallthrough */ + default: + rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby)", rb_id2name(name)); + break; } } else { -- cgit v1.2.1