From 7fc874bf4cbdd87b3d6fe05dc5959175f3fe94b8 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Tue, 3 Sep 2019 14:49:03 -0700 Subject: Add rb_funcall_with_block_kw This is needed for C functions to call methods with keyword arguments. This is a copy of rb_funcall_with_block with an extra argument for the keyword flag. There isn't a clean way to implement this that doesn't involve changing a lot of function signatures, because rb_call doesn't support a way to mark that the call has keyword arguments. So hack this in using a CALL_PUBLIC_KW call_type, which we switch for CALL_PUBLIC later in the call stack. We do need to modify rm_vm_call0 to take an argument for whether keyword arguments are used, since the call_type is no longer available at that point. Use the passed in value to set the appropriate keyword flag in both calling and ci_entry. --- include/ruby/ruby.h | 1 + vm_args.c | 4 ++-- vm_eval.c | 42 ++++++++++++++++++++++++++++++------------ vm_insnhelper.c | 4 ++-- vm_method.c | 2 +- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 947b3d44b8..893bd2b0b0 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1892,6 +1892,7 @@ VALUE rb_funcallv_public(VALUE, ID, int, const VALUE*); #define rb_funcall3 rb_funcallv_public VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*); VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE); +VALUE rb_funcall_with_block_kw(VALUE, ID, int, const VALUE*, VALUE, int); int rb_scan_args(int, const VALUE*, const char*, ...); VALUE rb_call_super(int, const VALUE*); VALUE rb_current_receiver(void); diff --git a/vm_args.c b/vm_args.c index 17eee5e65c..a80100ea3c 100644 --- a/vm_args.c +++ b/vm_args.c @@ -996,7 +996,7 @@ vm_to_proc(VALUE proc) rb_callable_method_entry_with_refinements(CLASS_OF(proc), idTo_proc, NULL); if (me) { - b = rb_vm_call0(GET_EC(), proc, idTo_proc, 0, NULL, me); + b = rb_vm_call0(GET_EC(), proc, idTo_proc, 0, NULL, me, VM_NO_KEYWORDS); } else { /* NOTE: calling method_missing */ @@ -1047,7 +1047,7 @@ refine_sym_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)) if (!me) { return method_missing(obj, mid, argc, argv, MISSING_NOENTRY); } - return rb_vm_call0(ec, obj, mid, argc, argv, me); + return rb_vm_call0(ec, obj, mid, argc, argv, me, VM_NO_KEYWORDS); } static VALUE diff --git a/vm_eval.c b/vm_eval.c index e826661480..4a304e33d3 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -32,6 +32,7 @@ typedef enum call_type { CALL_PUBLIC, CALL_FCALL, CALL_VCALL, + CALL_PUBLIC_KW, CALL_TYPE_MAX } call_type; @@ -41,7 +42,7 @@ static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *c #ifndef MJIT_HEADER MJIT_FUNC_EXPORTED VALUE -rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me) +rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me, int kw_splat) { struct rb_calling_info calling_entry, *calling; struct rb_call_info ci_entry; @@ -49,14 +50,14 @@ rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE calling = &calling_entry; - ci_entry.flag = 0; + ci_entry.flag = kw_splat ? VM_CALL_KW_SPLAT : 0; ci_entry.mid = id; cc_entry.me = me; calling->recv = recv; calling->argc = argc; - calling->kw_splat = 0; + calling->kw_splat = kw_splat; return vm_call0_body(ec, calling, &ci_entry, &cc_entry, argv); } @@ -206,7 +207,7 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE rb_vm_call(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me) { - return rb_vm_call0(ec, recv, id, argc, argv, me); + return rb_vm_call0(ec, recv, id, argc, argv, me, VM_NO_KEYWORDS); } static inline VALUE @@ -231,7 +232,7 @@ vm_call_super(rb_execution_context_t *ec, int argc, const VALUE *argv) return method_missing(recv, id, argc, argv, MISSING_SUPER); } else { - return rb_vm_call0(ec, recv, id, argc, argv, me); + return rb_vm_call0(ec, recv, id, argc, argv, me, VM_NO_KEYWORDS); } } @@ -290,10 +291,17 @@ static inline enum method_missing_reason rb_method_call_status(rb_execution_cont static inline VALUE rb_call0(rb_execution_context_t *ec, VALUE recv, ID mid, int argc, const VALUE *argv, - call_type scope, VALUE self) + call_type call_scope, VALUE self) { const rb_callable_method_entry_t *me; enum method_missing_reason call_status; + call_type scope = call_scope; + int kw_splat = VM_NO_KEYWORDS; + + if (scope == CALL_PUBLIC_KW) { + scope = CALL_PUBLIC; + kw_splat = 1; + } if (scope == CALL_PUBLIC) { me = rb_callable_method_entry_with_refinements(CLASS_OF(recv), mid, NULL); @@ -307,7 +315,7 @@ rb_call0(rb_execution_context_t *ec, return method_missing(recv, mid, argc, argv, call_status); } stack_check(ec); - return rb_vm_call0(ec, recv, mid, argc, argv, me); + return rb_vm_call0(ec, recv, mid, argc, argv, me, kw_splat); } struct rescue_funcall_args { @@ -434,7 +442,7 @@ rb_check_funcall_default(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE return ret; } stack_check(ec); - return rb_vm_call0(ec, recv, mid, argc, argv, me); + return rb_vm_call0(ec, recv, mid, argc, argv, me, VM_NO_KEYWORDS); } VALUE @@ -460,7 +468,7 @@ rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv, } stack_check(ec); (*hook)(TRUE, recv, mid, argc, argv, arg); - return rb_vm_call0(ec, recv, mid, argc, argv, me); + return rb_vm_call0(ec, recv, mid, argc, argv, me, VM_NO_KEYWORDS); } const char * @@ -766,7 +774,7 @@ method_missing(VALUE obj, ID id, int argc, const VALUE *argv, enum method_missin me = rb_callable_method_entry(klass, idMethodMissing); if (!me || METHOD_ENTRY_BASIC(me)) goto missing; vm_passed_block_handler_set(ec, block_handler); - result = rb_vm_call0(ec, obj, idMethodMissing, argc, argv, me); + result = rb_vm_call0(ec, obj, idMethodMissing, argc, argv, me, VM_NO_KEYWORDS); if (work) ALLOCV_END(work); return result; } @@ -884,7 +892,7 @@ rb_funcallv_with_cc(struct rb_call_cache_and_mid *cc, VALUE recv, ID mid, int ar vm_search_method(&ci, &cc->cc, recv); if (LIKELY(! UNDEFINED_METHOD_ENTRY_P(cc->cc.me))) { - return rb_vm_call0(GET_EC(), recv, mid, argc, argv, cc->cc.me); + return rb_vm_call0(GET_EC(), recv, mid, argc, argv, cc->cc.me, VM_NO_KEYWORDS); } } @@ -909,6 +917,16 @@ rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE pas return rb_call(recv, mid, argc, argv, CALL_PUBLIC); } +VALUE +rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval, int kw_splat) +{ + if (!NIL_P(passed_procval)) { + vm_passed_block_handler_set(GET_EC(), passed_procval); + } + + return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC); +} + static VALUE * current_vm_stack_arg(const rb_execution_context_t *ec, const VALUE *argv) { @@ -1601,7 +1619,7 @@ yield_under(VALUE under, VALUE self, int argc, const VALUE *argv) goto again; case block_handler_type_symbol: return rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)), - argc, argv, VM_BLOCK_HANDLER_NONE); + argc, argv, VM_NO_KEYWORDS, VM_BLOCK_HANDLER_NONE); } new_captured.self = self; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 2d10b58d00..de5a192e8b 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1576,7 +1576,7 @@ rb_eql_opt(VALUE obj1, VALUE obj2) return opt_eql_func(obj1, obj2, &ci, &cc); } -extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *); +extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat); static VALUE check_match(rb_execution_context_t *ec, VALUE pattern, VALUE target, enum vm_check_match_type type) @@ -1593,7 +1593,7 @@ check_match(rb_execution_context_t *ec, VALUE pattern, VALUE target, enum vm_che const rb_callable_method_entry_t *me = rb_callable_method_entry_with_refinements(CLASS_OF(pattern), idEqq, NULL); if (me) { - return rb_vm_call0(ec, pattern, idEqq, 1, &target, me); + return rb_vm_call0(ec, pattern, idEqq, 1, &target, me, VM_NO_KEYWORDS); } else { /* fallback to funcall (e.g. method_missing) */ diff --git a/vm_method.c b/vm_method.c index cd91cad802..7421bc96c2 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1921,7 +1921,7 @@ call_method_entry(rb_execution_context_t *ec, VALUE defined_class, VALUE obj, ID const rb_callable_method_entry_t *cme = prepare_callable_method_entry(defined_class, id, me); VALUE passed_block_handler = vm_passed_block_handler(ec); - VALUE result = rb_vm_call0(ec, obj, id, argc, argv, cme); + VALUE result = rb_vm_call0(ec, obj, id, argc, argv, cme, VM_NO_KEYWORDS); vm_passed_block_handler_set(ec, passed_block_handler); return result; } -- cgit v1.2.3