From 3cfbfa9628435e3b09316a18c2db9e4f250fdd77 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 13 Sep 2019 16:42:27 -0700 Subject: Consolidate empty keyword handling Remove rb_add_empty_keyword, and instead of calling that every place you need to add empty keyword hashes, run that code in a single static function in vm_eval.c. Add 4 defines to include/ruby/ruby.h, these are to be used as int kw_splat values when calling the various rb_*_kw functions: RB_NO_KEYWORDS :: Do not pass keywords RB_PASS_KEYWORDS :: Pass final argument (which should be hash) as keywords RB_PASS_EMPTY_KEYWORDS :: Add an empty hash to arguments and pass as keywords RB_PASS_CALLED_KEYWORDS :: Passes same keyword type as current method was called with (for method delegation) rb_empty_keyword_given_p needs to stay. It is required if argument delegation is done but delayed to a later point, which Enumerator does. Use RB_PASS_CALLED_KEYWORDS in rb_call_super to correctly delegate keyword arguments to super method. --- enumerator.c | 10 ++-------- eval.c | 15 +-------------- include/ruby/ruby.h | 4 ++++ internal.h | 1 - proc.c | 10 ++-------- vm_eval.c | 26 +++++++++++++++++++++++++- 6 files changed, 34 insertions(+), 32 deletions(-) diff --git a/enumerator.c b/enumerator.c index 43008bc170..2a94aa388f 100644 --- a/enumerator.c +++ b/enumerator.c @@ -370,7 +370,7 @@ enumerator_allocate(VALUE klass) return enum_obj; } -#define PASS_KW_SPLAT (rb_empty_keyword_given_p() ? 2 : rb_keyword_given_p()) +#define PASS_KW_SPLAT (rb_empty_keyword_given_p() ? RB_PASS_EMPTY_KEYWORDS : rb_keyword_given_p()) static VALUE enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, VALUE size, int kw_splat) @@ -386,13 +386,7 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *ar ptr->obj = obj; ptr->meth = rb_to_id(meth); - if (kw_splat == 2) { - if (argc) ptr->args = rb_ary_new4(argc+1, rb_add_empty_keyword(argc, argv)); - else ptr->args = rb_ary_new_from_args(1, rb_hash_new()); - kw_splat = 1; - } else { - if (argc) ptr->args = rb_ary_new4(argc, argv); - } + if (argc) ptr->args = rb_ary_new4(argc, argv); ptr->fib = 0; ptr->dst = Qnil; ptr->lookahead = Qundef; diff --git a/eval.c b/eval.c index fbdde7da9b..ca4456d5d2 100644 --- a/eval.c +++ b/eval.c @@ -914,14 +914,6 @@ rb_empty_keyword_given_p(void) { return rb_vm_cframe_empty_keyword_p(GET_EC()->cfp); } -VALUE * -rb_add_empty_keyword(int argc, const VALUE *argv) -{ - VALUE *ptr = ALLOC_N(VALUE,argc+1); - memcpy(ptr, argv, sizeof(VALUE)*(argc)); - ptr[argc] = rb_hash_new(); - return ptr; -} VALUE rb_eThreadError; @@ -1680,12 +1672,7 @@ void rb_obj_call_init(VALUE obj, int argc, const VALUE *argv) { PASS_PASSED_BLOCK_HANDLER(); - if (rb_empty_keyword_given_p()) { - rb_funcallv_kw(obj, idInitialize, argc+1, rb_add_empty_keyword(argc, argv), 1); - } - else { - rb_funcallv_kw(obj, idInitialize, argc, argv, rb_keyword_given_p()); - } + rb_funcallv_kw(obj, idInitialize, argc, argv, RB_PASS_CALLED_KEYWORDS); } /*! diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 0bede6ffdd..1b396f4c19 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1967,6 +1967,10 @@ VALUE rb_yield_values(int n, ...); VALUE rb_yield_values2(int n, const VALUE *argv); VALUE rb_yield_splat(VALUE); VALUE rb_yield_block(VALUE, VALUE, int, const VALUE *, VALUE); /* rb_block_call_func */ +#define RB_NO_KEYWORDS 0 +#define RB_PASS_KEYWORDS 1 +#define RB_PASS_EMPTY_KEYWORDS 2 +#define RB_PASS_CALLED_KEYWORDS 3 int rb_keyword_given_p(void); int rb_block_given_p(void); void rb_need_block(void); diff --git a/internal.h b/internal.h index cf0c3c94fc..3dbe9ad38e 100644 --- a/internal.h +++ b/internal.h @@ -1554,7 +1554,6 @@ NORETURN(VALUE rb_f_raise(int argc, VALUE *argv)); /* -- Remove In 3.0 -- */ int rb_empty_keyword_given_p(void); -VALUE * rb_add_empty_keyword(int argc, const VALUE *argv); /* eval_error.c */ VALUE rb_get_backtrace(VALUE info); diff --git a/proc.c b/proc.c index 3c65d3d0ac..92264af2a7 100644 --- a/proc.c +++ b/proc.c @@ -2223,14 +2223,8 @@ call_method_data(rb_execution_context_t *ec, const struct METHOD *data, int argc, const VALUE *argv, VALUE passed_procval) { vm_passed_block_handler_set(ec, proc_to_block_handler(passed_procval)); - if (rb_empty_keyword_given_p()) { - return rb_vm_call_kw(ec, data->recv, data->me->called_id, argc+1, rb_add_empty_keyword(argc, argv), - method_callable_method_entry(data), 1); - } - else { - return rb_vm_call_kw(ec, data->recv, data->me->called_id, argc, argv, - method_callable_method_entry(data), rb_keyword_given_p()); - } + return rb_vm_call_kw(ec, data->recv, data->me->called_id, argc, argv, + method_callable_method_entry(data), RB_PASS_CALLED_KEYWORDS); } static VALUE diff --git a/vm_eval.c b/vm_eval.c index 434e1dcd16..7609d191ef 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -235,6 +235,26 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const return ret; } +static void +add_empty_keyword(int *argc, const VALUE **argv, int *kw_splat) +{ + if (*kw_splat == RB_PASS_CALLED_KEYWORDS || *kw_splat == RB_PASS_EMPTY_KEYWORDS) { + if (*kw_splat == RB_PASS_EMPTY_KEYWORDS || rb_empty_keyword_given_p()) { + int n = *argc; + VALUE *ptr = ALLOC_N(VALUE,n+1); + + memcpy(ptr, *argv, sizeof(VALUE)*n); + ptr[n] = rb_hash_new(); + *argc = ++n; + *argv = ptr; + *kw_splat = 1; + } + else { + *kw_splat = rb_keyword_given_p(); + } + } +} + 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) { @@ -244,6 +264,7 @@ rb_vm_call(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc, const VAL VALUE rb_vm_call_kw(rb_execution_context_t *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const rb_callable_method_entry_t *me, int kw_splat) { + add_empty_keyword(&argc, &argv, &kw_splat); return rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat); } @@ -269,7 +290,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, VM_NO_KEYWORDS); + return rb_vm_call0(ec, recv, id, argc, argv, me, RB_PASS_CALLED_KEYWORDS); } } @@ -909,6 +930,7 @@ rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv) VALUE rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat) { + add_empty_keyword(&argc, &argv, &kw_splat); return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL); } @@ -976,6 +998,7 @@ rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE vm_passed_block_handler_set(GET_EC(), passed_procval); } + add_empty_keyword(&argc, &argv, &kw_splat); return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC); } @@ -1340,6 +1363,7 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv, { struct iter_method_arg arg; + add_empty_keyword(&argc, &argv, &kw_splat); arg.obj = obj; arg.mid = mid; arg.argc = argc; -- cgit v1.2.3