mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Fully separate positional arguments and keyword arguments
This removes the warnings added in 2.7, and changes the behavior so that a final positional hash is not treated as keywords or vice-versa. To handle the arg_setup_block splat case correctly with keyword arguments, we need to check if we are taking a keyword hash. That case didn't have a test, but it affects real-world code, so add a test for it. This removes rb_empty_keyword_given_p() and related code, as that is not needed in Ruby 3. The empty keyword case is the same as the no keyword case in Ruby 3. This changes rb_scan_args to implement keyword argument separation for C functions when the : character is used. For backwards compatibility, it returns a duped hash. This is a bad idea for performance, but not duping the hash breaks at least Enumerator::ArithmeticSequence#inspect. Instead of having RB_PASS_CALLED_KEYWORDS be a number, simplify the code by just making it be rb_keyword_given_p().
This commit is contained in:
parent
8ba261c754
commit
beae6cbf0f
Notes:
git
2020-01-03 11:41:10 +09:00
16 changed files with 96 additions and 679 deletions
70
class.c
70
class.c
|
@ -1970,7 +1970,6 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
|
||||||
const char *p = fmt;
|
const char *p = fmt;
|
||||||
VALUE *tmp_buffer = arg->tmp_buffer;
|
VALUE *tmp_buffer = arg->tmp_buffer;
|
||||||
int keyword_given = 0;
|
int keyword_given = 0;
|
||||||
int empty_keyword_given = 0;
|
|
||||||
int last_hash_keyword = 0;
|
int last_hash_keyword = 0;
|
||||||
|
|
||||||
memset(arg, 0, sizeof(*arg));
|
memset(arg, 0, sizeof(*arg));
|
||||||
|
@ -1979,16 +1978,11 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
|
||||||
|
|
||||||
switch (kw_flag) {
|
switch (kw_flag) {
|
||||||
case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS:
|
case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS:
|
||||||
if (!(keyword_given = rb_keyword_given_p())) {
|
keyword_given = rb_keyword_given_p();
|
||||||
empty_keyword_given = rb_empty_keyword_given_p();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case RB_SCAN_ARGS_KEYWORDS:
|
case RB_SCAN_ARGS_KEYWORDS:
|
||||||
keyword_given = 1;
|
keyword_given = 1;
|
||||||
break;
|
break;
|
||||||
case RB_SCAN_ARGS_EMPTY_KEYWORDS:
|
|
||||||
empty_keyword_given = 1;
|
|
||||||
break;
|
|
||||||
case RB_SCAN_ARGS_LAST_HASH_KEYWORDS:
|
case RB_SCAN_ARGS_LAST_HASH_KEYWORDS:
|
||||||
last_hash_keyword = 1;
|
last_hash_keyword = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -2023,67 +2017,13 @@ rb_scan_args_parse(int kw_flag, int argc, const VALUE *argv, const char *fmt, st
|
||||||
}
|
}
|
||||||
arg->n_mand = arg->n_lead + arg->n_trail;
|
arg->n_mand = arg->n_lead + arg->n_trail;
|
||||||
|
|
||||||
/* capture an option hash - phase 1: pop */
|
if (arg->f_hash && argc > 0) {
|
||||||
/* Ignore final positional hash if empty keywords given */
|
|
||||||
if (argc > 0 && !(arg->f_hash && empty_keyword_given)) {
|
|
||||||
VALUE last = argv[argc - 1];
|
VALUE last = argv[argc - 1];
|
||||||
|
|
||||||
if (arg->f_hash && arg->n_mand < argc) {
|
if (keyword_given || (last_hash_keyword && RB_TYPE_P(last, T_HASH))) {
|
||||||
if (keyword_given) {
|
arg->hash = rb_hash_dup(last);
|
||||||
if (!RB_TYPE_P(last, T_HASH)) {
|
argc--;
|
||||||
rb_warn("Keyword flag set when calling rb_scan_args, but last entry is not a hash");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arg->hash = last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (NIL_P(last)) {
|
|
||||||
/* For backwards compatibility, nil is taken as an empty
|
|
||||||
option hash only if it is not ambiguous; i.e. '*' is
|
|
||||||
not specified and arguments are given more than sufficient.
|
|
||||||
This will be removed in Ruby 3. */
|
|
||||||
if (!arg->f_var && arg->n_mand + arg->n_opt < argc) {
|
|
||||||
rb_warn("The last argument is nil, treating as empty keywords");
|
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arg->hash = rb_check_hash_type(last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ruby 3: Remove if branch, as it will not attempt to split hashes */
|
|
||||||
if (!NIL_P(arg->hash)) {
|
|
||||||
VALUE opts = rb_extract_keywords(&arg->hash);
|
|
||||||
|
|
||||||
if (!(arg->last_hash = arg->hash)) {
|
|
||||||
if (!keyword_given && !last_hash_keyword) {
|
|
||||||
/* Warn if treating positional as keyword, as in Ruby 3,
|
|
||||||
this will be an error */
|
|
||||||
rb_warn("Using the last argument as keyword parameters is deprecated");
|
|
||||||
}
|
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Warn if splitting either positional hash to keywords or keywords
|
|
||||||
to positional hash, as in Ruby 3, no splitting will be done */
|
|
||||||
rb_warn("The last argument is split into positional and keyword parameters");
|
|
||||||
arg->last_idx = argc - 1;
|
|
||||||
}
|
|
||||||
arg->hash = opts ? opts : Qnil;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (arg->f_hash && keyword_given && arg->n_mand == argc) {
|
|
||||||
/* Warn if treating keywords as positional, as in Ruby 3, this will be an error */
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (arg->f_hash && arg->n_mand == argc+1 && empty_keyword_given) {
|
|
||||||
VALUE *ptr = rb_alloc_tmp_buffer2(tmp_buffer, argc+1, sizeof(VALUE));
|
|
||||||
memcpy(ptr, argv, sizeof(VALUE)*argc);
|
|
||||||
ptr[argc] = rb_hash_new();
|
|
||||||
argc++;
|
|
||||||
*(&argv) = ptr;
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
arg->argc = argc;
|
arg->argc = argc;
|
||||||
|
|
12
cont.c
12
cont.c
|
@ -1790,8 +1790,6 @@ rb_fiber_new(rb_block_call_func_t func, VALUE obj)
|
||||||
|
|
||||||
static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt);
|
static void rb_fiber_terminate(rb_fiber_t *fiber, int need_interrupt);
|
||||||
|
|
||||||
#define PASS_KW_SPLAT (rb_empty_keyword_given_p() ? RB_PASS_EMPTY_KEYWORDS : rb_keyword_given_p())
|
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_fiber_start(void)
|
rb_fiber_start(void)
|
||||||
{
|
{
|
||||||
|
@ -1809,7 +1807,6 @@ rb_fiber_start(void)
|
||||||
rb_context_t *cont = &VAR_FROM_MEMORY(fiber)->cont;
|
rb_context_t *cont = &VAR_FROM_MEMORY(fiber)->cont;
|
||||||
int argc;
|
int argc;
|
||||||
const VALUE *argv, args = cont->value;
|
const VALUE *argv, args = cont->value;
|
||||||
int kw_splat = cont->kw_splat;
|
|
||||||
GetProcPtr(fiber->first_proc, proc);
|
GetProcPtr(fiber->first_proc, proc);
|
||||||
argv = (argc = cont->argc) > 1 ? RARRAY_CONST_PTR(args) : &args;
|
argv = (argc = cont->argc) > 1 ? RARRAY_CONST_PTR(args) : &args;
|
||||||
cont->value = Qnil;
|
cont->value = Qnil;
|
||||||
|
@ -1818,8 +1815,7 @@ rb_fiber_start(void)
|
||||||
th->ec->root_svar = Qfalse;
|
th->ec->root_svar = Qfalse;
|
||||||
|
|
||||||
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
|
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
|
||||||
rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, cont->kw_splat, VM_BLOCK_HANDLER_NONE);
|
||||||
cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, kw_splat, VM_BLOCK_HANDLER_NONE);
|
|
||||||
}
|
}
|
||||||
EC_POP_TAG();
|
EC_POP_TAG();
|
||||||
|
|
||||||
|
@ -2163,7 +2159,7 @@ rb_fiber_alive_p(VALUE fiber_value)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber)
|
rb_fiber_m_resume(int argc, VALUE *argv, VALUE fiber)
|
||||||
{
|
{
|
||||||
return rb_fiber_resume_kw(fiber, argc, argv, PASS_KW_SPLAT);
|
return rb_fiber_resume_kw(fiber, argc, argv, rb_keyword_given_p());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2249,7 +2245,7 @@ rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fiber_value)
|
||||||
{
|
{
|
||||||
rb_fiber_t *fiber = fiber_ptr(fiber_value);
|
rb_fiber_t *fiber = fiber_ptr(fiber_value);
|
||||||
fiber->transferred = 1;
|
fiber->transferred = 1;
|
||||||
return fiber_switch(fiber, argc, argv, 0, PASS_KW_SPLAT);
|
return fiber_switch(fiber, argc, argv, 0, rb_keyword_given_p());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2265,7 +2261,7 @@ rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fiber_value)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
|
rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
|
||||||
{
|
{
|
||||||
return rb_fiber_yield_kw(argc, argv, PASS_KW_SPLAT);
|
return rb_fiber_yield_kw(argc, argv, rb_keyword_given_p());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
14
enumerator.c
14
enumerator.c
|
@ -384,8 +384,6 @@ enumerator_allocate(VALUE klass)
|
||||||
return enum_obj;
|
return enum_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PASS_KW_SPLAT (rb_empty_keyword_given_p() ? RB_PASS_EMPTY_KEYWORDS : rb_keyword_given_p())
|
|
||||||
|
|
||||||
static VALUE
|
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)
|
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)
|
||||||
{
|
{
|
||||||
|
@ -480,7 +478,7 @@ enumerator_initialize(int argc, VALUE *argv, VALUE obj)
|
||||||
meth = *argv++;
|
meth = *argv++;
|
||||||
--argc;
|
--argc;
|
||||||
}
|
}
|
||||||
kw_splat = PASS_KW_SPLAT;
|
kw_splat = rb_keyword_given_p();
|
||||||
}
|
}
|
||||||
|
|
||||||
return enumerator_init(obj, recv, meth, argc, argv, 0, size, kw_splat);
|
return enumerator_init(obj, recv, meth, argc, argv, 0, size, kw_splat);
|
||||||
|
@ -535,10 +533,10 @@ rb_enumeratorize_with_size(VALUE obj, VALUE meth, int argc, const VALUE *argv, r
|
||||||
/* Similar effect as calling obj.to_enum, i.e. dispatching to either
|
/* Similar effect as calling obj.to_enum, i.e. dispatching to either
|
||||||
Kernel#to_enum vs Lazy#to_enum */
|
Kernel#to_enum vs Lazy#to_enum */
|
||||||
if (RTEST(rb_obj_is_kind_of(obj, rb_cLazy)))
|
if (RTEST(rb_obj_is_kind_of(obj, rb_cLazy)))
|
||||||
return lazy_to_enum_i(obj, meth, argc, argv, size_fn, PASS_KW_SPLAT);
|
return lazy_to_enum_i(obj, meth, argc, argv, size_fn, rb_keyword_given_p());
|
||||||
else
|
else
|
||||||
return enumerator_init(enumerator_allocate(rb_cEnumerator),
|
return enumerator_init(enumerator_allocate(rb_cEnumerator),
|
||||||
obj, meth, argc, argv, size_fn, Qnil, PASS_KW_SPLAT);
|
obj, meth, argc, argv, size_fn, Qnil, rb_keyword_given_p());
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
|
@ -1892,7 +1890,7 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo,
|
||||||
static VALUE
|
static VALUE
|
||||||
enumerable_lazy(VALUE obj)
|
enumerable_lazy(VALUE obj)
|
||||||
{
|
{
|
||||||
VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size, PASS_KW_SPLAT);
|
VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size, rb_keyword_given_p());
|
||||||
/* Qfalse indicates that the Enumerator::Lazy has no method name */
|
/* Qfalse indicates that the Enumerator::Lazy has no method name */
|
||||||
rb_ivar_set(result, id_method, Qfalse);
|
rb_ivar_set(result, id_method, Qfalse);
|
||||||
return result;
|
return result;
|
||||||
|
@ -1940,7 +1938,7 @@ lazy_to_enum(int argc, VALUE *argv, VALUE self)
|
||||||
if (RTEST((super_meth = rb_hash_aref(lazy_use_super_method, meth)))) {
|
if (RTEST((super_meth = rb_hash_aref(lazy_use_super_method, meth)))) {
|
||||||
meth = super_meth;
|
meth = super_meth;
|
||||||
}
|
}
|
||||||
lazy = lazy_to_enum_i(self, meth, argc, argv, 0, PASS_KW_SPLAT);
|
lazy = lazy_to_enum_i(self, meth, argc, argv, 0, rb_keyword_given_p());
|
||||||
if (rb_block_given_p()) {
|
if (rb_block_given_p()) {
|
||||||
enumerator_ptr(lazy)->size = rb_block_proc();
|
enumerator_ptr(lazy)->size = rb_block_proc();
|
||||||
}
|
}
|
||||||
|
@ -3318,7 +3316,7 @@ rb_arith_seq_new(VALUE obj, VALUE meth, int argc, VALUE const *argv,
|
||||||
VALUE beg, VALUE end, VALUE step, int excl)
|
VALUE beg, VALUE end, VALUE step, int excl)
|
||||||
{
|
{
|
||||||
VALUE aseq = enumerator_init(enumerator_allocate(rb_cArithSeq),
|
VALUE aseq = enumerator_init(enumerator_allocate(rb_cArithSeq),
|
||||||
obj, meth, argc, argv, size_fn, Qnil, PASS_KW_SPLAT);
|
obj, meth, argc, argv, size_fn, Qnil, rb_keyword_given_p());
|
||||||
rb_ivar_set(aseq, id_begin, beg);
|
rb_ivar_set(aseq, id_begin, beg);
|
||||||
rb_ivar_set(aseq, id_end, end);
|
rb_ivar_set(aseq, id_end, end);
|
||||||
rb_ivar_set(aseq, id_step, step);
|
rb_ivar_set(aseq, id_step, step);
|
||||||
|
|
8
eval.c
8
eval.c
|
@ -924,14 +924,6 @@ rb_keyword_given_p(void)
|
||||||
return rb_vm_cframe_keyword_p(GET_EC()->cfp);
|
return rb_vm_cframe_keyword_p(GET_EC()->cfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Remove In 3.0 -- */
|
|
||||||
int rb_vm_cframe_empty_keyword_p(const rb_control_frame_t *cfp);
|
|
||||||
int
|
|
||||||
rb_empty_keyword_given_p(void)
|
|
||||||
{
|
|
||||||
return rb_vm_cframe_empty_keyword_p(GET_EC()->cfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE rb_eThreadError;
|
VALUE rb_eThreadError;
|
||||||
|
|
||||||
/*! Declares that the current method needs a block.
|
/*! Declares that the current method needs a block.
|
||||||
|
|
|
@ -259,15 +259,6 @@ scan_args_k_lead_opt_hash(int argc, VALUE *argv, VALUE self)
|
||||||
return rb_ary_new_from_values(numberof(args), args);
|
return rb_ary_new_from_values(numberof(args), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
|
||||||
scan_args_e_lead_opt_hash(int argc, VALUE *argv, VALUE self)
|
|
||||||
{
|
|
||||||
VALUE args[4];
|
|
||||||
int n = rb_scan_args_kw(RB_SCAN_ARGS_EMPTY_KEYWORDS, argc, argv, "11:", args+1, args+2, args+3);
|
|
||||||
args[0] = INT2NUM(n);
|
|
||||||
return rb_ary_new_from_values(numberof(args), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
scan_args_n_lead_opt_hash(int argc, VALUE *argv, VALUE self)
|
scan_args_n_lead_opt_hash(int argc, VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
|
@ -310,6 +301,5 @@ Init_scan_args(void)
|
||||||
rb_define_singleton_method(module, "opt_var_trail_hash", scan_args_opt_var_trail_hash, -1);
|
rb_define_singleton_method(module, "opt_var_trail_hash", scan_args_opt_var_trail_hash, -1);
|
||||||
rb_define_singleton_method(module, "lead_opt_var_trail_hash", scan_args_lead_opt_var_trail_hash, -1);
|
rb_define_singleton_method(module, "lead_opt_var_trail_hash", scan_args_lead_opt_var_trail_hash, -1);
|
||||||
rb_define_singleton_method(module, "k_lead_opt_hash", scan_args_k_lead_opt_hash, -1);
|
rb_define_singleton_method(module, "k_lead_opt_hash", scan_args_k_lead_opt_hash, -1);
|
||||||
rb_define_singleton_method(module, "e_lead_opt_hash", scan_args_e_lead_opt_hash, -1);
|
|
||||||
rb_define_singleton_method(module, "n_lead_opt_hash", scan_args_n_lead_opt_hash, -1);
|
rb_define_singleton_method(module, "n_lead_opt_hash", scan_args_n_lead_opt_hash, -1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1901,7 +1901,6 @@ VALUE rb_funcall_with_block_kw(VALUE, ID, int, const VALUE*, VALUE, int);
|
||||||
int rb_scan_args(int, const VALUE*, const char*, ...);
|
int rb_scan_args(int, const VALUE*, const char*, ...);
|
||||||
#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
|
#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
|
||||||
#define RB_SCAN_ARGS_KEYWORDS 1
|
#define RB_SCAN_ARGS_KEYWORDS 1
|
||||||
#define RB_SCAN_ARGS_EMPTY_KEYWORDS 2 /* Will be removed in 3.0 */
|
|
||||||
#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
|
#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
|
||||||
int rb_scan_args_kw(int, int, const VALUE*, const char*, ...);
|
int rb_scan_args_kw(int, int, const VALUE*, const char*, ...);
|
||||||
VALUE rb_call_super(int, const VALUE*);
|
VALUE rb_call_super(int, const VALUE*);
|
||||||
|
@ -1976,8 +1975,7 @@ VALUE rb_yield_splat_kw(VALUE, int);
|
||||||
VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
|
VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
|
||||||
#define RB_NO_KEYWORDS 0
|
#define RB_NO_KEYWORDS 0
|
||||||
#define RB_PASS_KEYWORDS 1
|
#define RB_PASS_KEYWORDS 1
|
||||||
#define RB_PASS_EMPTY_KEYWORDS 2 /* Will be removed in 3.0 */
|
#define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p()
|
||||||
#define RB_PASS_CALLED_KEYWORDS 3
|
|
||||||
int rb_keyword_given_p(void);
|
int rb_keyword_given_p(void);
|
||||||
int rb_block_given_p(void);
|
int rb_block_given_p(void);
|
||||||
void rb_need_block(void);
|
void rb_need_block(void);
|
||||||
|
@ -2331,9 +2329,6 @@ unsigned long ruby_strtoul(const char *str, char **endptr, int base);
|
||||||
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
|
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
|
||||||
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
|
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
|
||||||
|
|
||||||
/* -- Remove In 3.0, Only public for rb_scan_args optimized version -- */
|
|
||||||
int rb_empty_keyword_given_p(void);
|
|
||||||
|
|
||||||
#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
|
#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
|
||||||
# define rb_scan_args(argc,argvp,fmt,...) \
|
# define rb_scan_args(argc,argvp,fmt,...) \
|
||||||
__builtin_choose_expr(__builtin_constant_p(fmt), \
|
__builtin_choose_expr(__builtin_constant_p(fmt), \
|
||||||
|
@ -2525,78 +2520,13 @@ rb_scan_args_set(int argc, const VALUE *argv,
|
||||||
int i, argi = 0, vari = 0, last_idx = -1;
|
int i, argi = 0, vari = 0, last_idx = -1;
|
||||||
VALUE *var, hash = Qnil, last_hash = 0;
|
VALUE *var, hash = Qnil, last_hash = 0;
|
||||||
const int n_mand = n_lead + n_trail;
|
const int n_mand = n_lead + n_trail;
|
||||||
int keyword_given = rb_keyword_given_p();
|
|
||||||
int empty_keyword_given = 0;
|
|
||||||
VALUE tmp_buffer = 0;
|
VALUE tmp_buffer = 0;
|
||||||
|
|
||||||
if (!keyword_given) {
|
if (f_hash && argc > 0 && rb_keyword_given_p()) {
|
||||||
empty_keyword_given = rb_empty_keyword_given_p();
|
hash = rb_hash_dup(argv[argc - 1]);
|
||||||
|
argc--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* capture an option hash - phase 1: pop */
|
|
||||||
/* Ignore final positional hash if empty keywords given */
|
|
||||||
if (argc > 0 && !(f_hash && empty_keyword_given)) {
|
|
||||||
VALUE last = argv[argc - 1];
|
|
||||||
|
|
||||||
if (f_hash && n_mand < argc) {
|
|
||||||
if (keyword_given) {
|
|
||||||
if (!RB_TYPE_P(last, T_HASH)) {
|
|
||||||
rb_warn("Keyword flag set when calling rb_scan_args, but last entry is not a hash");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hash = last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (NIL_P(last)) {
|
|
||||||
/* For backwards compatibility, nil is taken as an empty
|
|
||||||
option hash only if it is not ambiguous; i.e. '*' is
|
|
||||||
not specified and arguments are given more than sufficient.
|
|
||||||
This will be removed in Ruby 3. */
|
|
||||||
if (!f_var && n_mand + n_opt < argc) {
|
|
||||||
rb_warn("The last argument is nil, treating as empty keywords");
|
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hash = rb_check_hash_type(last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ruby 3: Remove if branch, as it will not attempt to split hashes */
|
|
||||||
if (!NIL_P(hash)) {
|
|
||||||
VALUE opts = rb_extract_keywords(&hash);
|
|
||||||
|
|
||||||
if (!(last_hash = hash)) {
|
|
||||||
if (!keyword_given) {
|
|
||||||
/* Warn if treating positional as keyword, as in Ruby 3,
|
|
||||||
this will be an error */
|
|
||||||
rb_warn("Using the last argument as keyword parameters is deprecated");
|
|
||||||
}
|
|
||||||
argc--;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Warn if splitting either positional hash to keywords or keywords
|
|
||||||
to positional hash, as in Ruby 3, no splitting will be done */
|
|
||||||
rb_warn("The last argument is split into positional and keyword parameters");
|
|
||||||
last_idx = argc - 1;
|
|
||||||
}
|
|
||||||
hash = opts ? opts : Qnil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (f_hash && keyword_given && n_mand == argc) {
|
|
||||||
/* Warn if treating keywords as positional, as in Ruby 3, this will be an error */
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (f_hash && n_mand > 0 && n_mand == argc+1 && empty_keyword_given) {
|
|
||||||
VALUE *ptr = (VALUE *)rb_alloc_tmp_buffer2(&tmp_buffer, argc+1, sizeof(VALUE));
|
|
||||||
memcpy(ptr, argv, sizeof(VALUE)*argc);
|
|
||||||
ptr[argc] = rb_hash_new();
|
|
||||||
argc++;
|
|
||||||
*(&argv) = ptr;
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (argc < n_mand) {
|
if (argc < n_mand) {
|
||||||
goto argc_error;
|
goto argc_error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,6 @@ VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
|
||||||
|
|
||||||
MJIT_SYMBOL_EXPORT_BEGIN
|
MJIT_SYMBOL_EXPORT_BEGIN
|
||||||
VALUE rb_vm_call0(struct rb_execution_context_struct *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
|
VALUE rb_vm_call0(struct rb_execution_context_struct *ec, VALUE recv, ID id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
|
||||||
VALUE rb_adjust_argv_kw_splat(int *argc, const VALUE **argv, int *kw_splat);
|
|
||||||
VALUE rb_vm_call_kw(struct rb_execution_context_struct *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
|
VALUE rb_vm_call_kw(struct rb_execution_context_struct *ec, VALUE recv, VALUE id, int argc, const VALUE *argv, const struct rb_callable_method_entry_struct *me, int kw_splat);
|
||||||
VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj, int argc, const VALUE *argv, int priv);
|
VALUE rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj, int argc, const VALUE *argv, int priv);
|
||||||
MJIT_SYMBOL_EXPORT_END
|
MJIT_SYMBOL_EXPORT_END
|
||||||
|
|
4
object.c
4
object.c
|
@ -4086,9 +4086,7 @@ rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
|
||||||
}
|
}
|
||||||
return rb_check_funcall_with_hook_kw(obj, id_dig, argc, argv,
|
return rb_check_funcall_with_hook_kw(obj, id_dig, argc, argv,
|
||||||
no_dig_method, obj,
|
no_dig_method, obj,
|
||||||
rb_empty_keyword_given_p() ?
|
RB_NO_KEYWORDS);
|
||||||
RB_PASS_EMPTY_KEYWORDS :
|
|
||||||
RB_NO_KEYWORDS);
|
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
5
proc.c
5
proc.c
|
@ -955,14 +955,11 @@ rb_proc_call_kw(VALUE self, VALUE args, int kw_splat)
|
||||||
{
|
{
|
||||||
VALUE vret;
|
VALUE vret;
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
VALUE v;
|
|
||||||
int argc = check_argc(RARRAY_LEN(args));
|
int argc = check_argc(RARRAY_LEN(args));
|
||||||
const VALUE *argv = RARRAY_CONST_PTR(args);
|
const VALUE *argv = RARRAY_CONST_PTR(args);
|
||||||
GetProcPtr(self, proc);
|
GetProcPtr(self, proc);
|
||||||
v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
vret = rb_vm_invoke_proc(GET_EC(), proc, argc, argv,
|
vret = rb_vm_invoke_proc(GET_EC(), proc, argc, argv,
|
||||||
kw_splat, VM_BLOCK_HANDLER_NONE);
|
kw_splat, VM_BLOCK_HANDLER_NONE);
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
RB_GC_GUARD(self);
|
RB_GC_GUARD(self);
|
||||||
RB_GC_GUARD(args);
|
RB_GC_GUARD(args);
|
||||||
return vret;
|
return vret;
|
||||||
|
@ -994,10 +991,8 @@ rb_proc_call_with_block_kw(VALUE self, int argc, const VALUE *argv, VALUE passed
|
||||||
rb_execution_context_t *ec = GET_EC();
|
rb_execution_context_t *ec = GET_EC();
|
||||||
VALUE vret;
|
VALUE vret;
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
GetProcPtr(self, proc);
|
GetProcPtr(self, proc);
|
||||||
vret = rb_vm_invoke_proc(ec, proc, argc, argv, kw_splat, proc_to_block_handler(passed_procval));
|
vret = rb_vm_invoke_proc(ec, proc, argc, argv, kw_splat, proc_to_block_handler(passed_procval));
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
RB_GC_GUARD(self);
|
RB_GC_GUARD(self);
|
||||||
return vret;
|
return vret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1087,6 +1087,16 @@ class TestProc < Test::Unit::TestCase
|
||||||
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
|
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_proc_single_arg_with_keywords_accepted_and_yielded
|
||||||
|
def self.a
|
||||||
|
yield [], **{a: 1}
|
||||||
|
end
|
||||||
|
res = a do |arg, **opts|
|
||||||
|
[arg, opts]
|
||||||
|
end
|
||||||
|
assert_equal([[], {a: 1}], res)
|
||||||
|
end
|
||||||
|
|
||||||
def test_parameters
|
def test_parameters
|
||||||
assert_equal([], proc {}.parameters)
|
assert_equal([], proc {}.parameters)
|
||||||
assert_equal([], proc {||}.parameters)
|
assert_equal([], proc {||}.parameters)
|
||||||
|
|
10
thread.c
10
thread.c
|
@ -681,7 +681,6 @@ thread_do_start(rb_thread_t *th)
|
||||||
if (th->invoke_type == thread_invoke_type_proc) {
|
if (th->invoke_type == thread_invoke_type_proc) {
|
||||||
VALUE args = th->invoke_arg.proc.args;
|
VALUE args = th->invoke_arg.proc.args;
|
||||||
int args_len = (int)RARRAY_LEN(args);
|
int args_len = (int)RARRAY_LEN(args);
|
||||||
int kw_splat = th->invoke_arg.proc.kw_splat;
|
|
||||||
const VALUE *args_ptr;
|
const VALUE *args_ptr;
|
||||||
VALUE procval = th->invoke_arg.proc.proc;
|
VALUE procval = th->invoke_arg.proc.proc;
|
||||||
rb_proc_t *proc;
|
rb_proc_t *proc;
|
||||||
|
@ -704,10 +703,11 @@ thread_do_start(rb_thread_t *th)
|
||||||
args_ptr = RARRAY_CONST_PTR(args);
|
args_ptr = RARRAY_CONST_PTR(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_adjust_argv_kw_splat(&args_len, &args_ptr, &kw_splat);
|
vm_check_ints_blocking(th->ec);
|
||||||
th->value = rb_vm_invoke_proc(th->ec, proc,
|
th->value = rb_vm_invoke_proc(th->ec, proc,
|
||||||
args_len, args_ptr,
|
args_len, args_ptr,
|
||||||
kw_splat, VM_BLOCK_HANDLER_NONE);
|
th->invoke_arg.proc.kw_splat,
|
||||||
|
VM_BLOCK_HANDLER_NONE);
|
||||||
|
|
||||||
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
|
EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
|
||||||
}
|
}
|
||||||
|
@ -856,9 +856,7 @@ thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(void *))
|
||||||
th->invoke_type = thread_invoke_type_proc;
|
th->invoke_type = thread_invoke_type_proc;
|
||||||
th->invoke_arg.proc.proc = rb_block_proc();
|
th->invoke_arg.proc.proc = rb_block_proc();
|
||||||
th->invoke_arg.proc.args = args;
|
th->invoke_arg.proc.args = args;
|
||||||
th->invoke_arg.proc.kw_splat = rb_empty_keyword_given_p() ?
|
th->invoke_arg.proc.kw_splat = rb_keyword_given_p();
|
||||||
RB_PASS_EMPTY_KEYWORDS :
|
|
||||||
rb_keyword_given_p();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th->priority = current_th->priority;
|
th->priority = current_th->priority;
|
||||||
|
|
7
vm.c
7
vm.c
|
@ -119,13 +119,6 @@ rb_vm_cframe_keyword_p(const rb_control_frame_t *cfp)
|
||||||
return VM_FRAME_CFRAME_KW_P(cfp);
|
return VM_FRAME_CFRAME_KW_P(cfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Remove In 3.0 -- */
|
|
||||||
int
|
|
||||||
rb_vm_cframe_empty_keyword_p(const rb_control_frame_t *cfp)
|
|
||||||
{
|
|
||||||
return VM_FRAME_CFRAME_EMPTY_KW_P(cfp);
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_vm_frame_block_handler(const rb_control_frame_t *cfp)
|
rb_vm_frame_block_handler(const rb_control_frame_t *cfp)
|
||||||
{
|
{
|
||||||
|
|
383
vm_args.c
383
vm_args.c
|
@ -187,124 +187,6 @@ args_rest_array(struct args_info *args)
|
||||||
return ary;
|
return ary;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define KW_HASH_HAS_NO_KEYS 0
|
|
||||||
#define KW_HASH_HAS_SYMBOL_KEY 1
|
|
||||||
#define KW_HASH_HAS_OTHER_KEY 2
|
|
||||||
#define KW_HASH_HAS_BOTH_KEYS 3
|
|
||||||
|
|
||||||
static int
|
|
||||||
keyword_hash_symbol_other_iter(st_data_t key, st_data_t val, st_data_t arg)
|
|
||||||
{
|
|
||||||
*(int*)arg |= SYMBOL_P((VALUE)key) ? KW_HASH_HAS_SYMBOL_KEY : KW_HASH_HAS_OTHER_KEY;
|
|
||||||
|
|
||||||
if ((*(int*)arg & KW_HASH_HAS_BOTH_KEYS) == KW_HASH_HAS_BOTH_KEYS) {
|
|
||||||
return ST_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
keyword_hash_symbol_other(VALUE hash)
|
|
||||||
{
|
|
||||||
int symbol_other = KW_HASH_HAS_NO_KEYS;
|
|
||||||
rb_hash_stlike_foreach(hash, keyword_hash_symbol_other_iter, (st_data_t)(&symbol_other));
|
|
||||||
return symbol_other;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
keyword_hash_split_iter(st_data_t key, st_data_t val, st_data_t arg)
|
|
||||||
{
|
|
||||||
if (SYMBOL_P((VALUE)key)) {
|
|
||||||
rb_hash_aset((VALUE)arg, (VALUE)key, (VALUE)val);
|
|
||||||
return ST_DELETE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ST_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
keyword_hash_split(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr)
|
|
||||||
{
|
|
||||||
*kw_hash_ptr = rb_hash_new();
|
|
||||||
rb_hash_stlike_foreach(*rest_hash_ptr, keyword_hash_split_iter, (st_data_t)(*kw_hash_ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
keyword_hash_p(VALUE *kw_hash_ptr, VALUE *rest_hash_ptr, int check_only_symbol)
|
|
||||||
{
|
|
||||||
*rest_hash_ptr = rb_check_hash_type(*kw_hash_ptr);
|
|
||||||
|
|
||||||
if (!NIL_P(*rest_hash_ptr)) {
|
|
||||||
if (check_only_symbol) {
|
|
||||||
switch (keyword_hash_symbol_other(*rest_hash_ptr)) {
|
|
||||||
case KW_HASH_HAS_NO_KEYS:
|
|
||||||
case KW_HASH_HAS_SYMBOL_KEY:
|
|
||||||
break;
|
|
||||||
case KW_HASH_HAS_OTHER_KEY:
|
|
||||||
*kw_hash_ptr = Qnil;
|
|
||||||
return FALSE;
|
|
||||||
case KW_HASH_HAS_BOTH_KEYS:
|
|
||||||
*rest_hash_ptr = rb_hash_dup(*rest_hash_ptr);
|
|
||||||
keyword_hash_split(kw_hash_ptr, rest_hash_ptr);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*kw_hash_ptr = *rest_hash_ptr;
|
|
||||||
*rest_hash_ptr = Qfalse;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*kw_hash_ptr = Qnil;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static VALUE
|
|
||||||
args_pop_keyword_hash(struct args_info *args, VALUE *kw_hash_ptr, int check_only_symbol)
|
|
||||||
{
|
|
||||||
VALUE rest_hash;
|
|
||||||
|
|
||||||
if (args->rest == Qfalse) {
|
|
||||||
from_argv:
|
|
||||||
VM_ASSERT(args->argc > 0);
|
|
||||||
*kw_hash_ptr = args->argv[args->argc-1];
|
|
||||||
|
|
||||||
if (keyword_hash_p(kw_hash_ptr, &rest_hash, check_only_symbol)) {
|
|
||||||
if (rest_hash) {
|
|
||||||
args->argv[args->argc-1] = rest_hash;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
args->argc--;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
long len = RARRAY_LEN(args->rest);
|
|
||||||
|
|
||||||
if (len > 0) {
|
|
||||||
*kw_hash_ptr = RARRAY_AREF(args->rest, len - 1);
|
|
||||||
|
|
||||||
if (keyword_hash_p(kw_hash_ptr, &rest_hash, check_only_symbol)) {
|
|
||||||
if (rest_hash) {
|
|
||||||
RARRAY_ASET(args->rest, len - 1, rest_hash);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arg_rest_dup(args);
|
|
||||||
rb_ary_pop(args->rest);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto from_argv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
args_kw_argv_to_hash(struct args_info *args)
|
args_kw_argv_to_hash(struct args_info *args)
|
||||||
{
|
{
|
||||||
|
@ -572,149 +454,10 @@ fill_keys_values(st_data_t key, st_data_t val, st_data_t ptr)
|
||||||
static inline int
|
static inline int
|
||||||
ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq)
|
ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq)
|
||||||
{
|
{
|
||||||
if (!(iseq->body->param.flags.has_kw) &&
|
return !(iseq->body->param.flags.has_kw) &&
|
||||||
!(iseq->body->param.flags.has_kwrest)) {
|
!(iseq->body->param.flags.has_kwrest) &&
|
||||||
keyword_hash = rb_check_hash_type(keyword_hash);
|
RB_TYPE_P(keyword_hash, T_HASH) &&
|
||||||
|
RHASH_EMPTY_P(keyword_hash);
|
||||||
if (!NIL_P(keyword_hash) && RHASH_EMPTY_P(keyword_hash)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
VALUE rb_iseq_location(const rb_iseq_t *iseq);
|
|
||||||
|
|
||||||
/* -- Remove In 3.0 -- */
|
|
||||||
|
|
||||||
/* This is a map from caller PC to a set of callee methods.
|
|
||||||
* When a warning about keyword argument change is printed,
|
|
||||||
* it keeps the pair of callee and caller.
|
|
||||||
*/
|
|
||||||
static st_table *caller_to_callees = 0;
|
|
||||||
|
|
||||||
static VALUE
|
|
||||||
rb_warn_check(const rb_execution_context_t * const ec, const rb_iseq_t *const iseq)
|
|
||||||
{
|
|
||||||
if (!rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) return 1;
|
|
||||||
|
|
||||||
if (!iseq) return 0;
|
|
||||||
|
|
||||||
const st_data_t callee = (st_data_t)(iseq->body->iseq_unique_id * 2);
|
|
||||||
|
|
||||||
const rb_control_frame_t * const cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
|
|
||||||
|
|
||||||
if (!cfp) return 0;
|
|
||||||
|
|
||||||
const st_data_t caller = (st_data_t)cfp->pc;
|
|
||||||
|
|
||||||
if (!caller_to_callees) {
|
|
||||||
caller_to_callees = st_init_numtable();
|
|
||||||
}
|
|
||||||
|
|
||||||
st_data_t val;
|
|
||||||
if (st_lookup(caller_to_callees, caller, &val)) {
|
|
||||||
st_table *callees;
|
|
||||||
|
|
||||||
if (val & 1) {
|
|
||||||
val &= ~(st_data_t)1;
|
|
||||||
if (val == callee) return 1; /* already warned */
|
|
||||||
|
|
||||||
callees = st_init_numtable();
|
|
||||||
st_insert(callees, val, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callees = (st_table *) val;
|
|
||||||
if (st_is_member(callees, callee)) return 1; /* already warned */
|
|
||||||
}
|
|
||||||
st_insert(callees, callee, 1);
|
|
||||||
st_insert(caller_to_callees, caller, (st_data_t) callees);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
st_insert(caller_to_callees, caller, callee | 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; /* not warned yet for the pair of caller and callee */
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
rb_warn_keyword_to_last_hash(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
|
|
||||||
{
|
|
||||||
if (rb_warn_check(ec, iseq)) return;
|
|
||||||
|
|
||||||
VALUE name, loc;
|
|
||||||
if (calling->recv == Qundef) {
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
name = rb_id2str(ci->mid);
|
|
||||||
loc = rb_iseq_location(iseq);
|
|
||||||
if (NIL_P(loc)) {
|
|
||||||
rb_warn("Passing the keyword argument for `%"PRIsVALUE"' as the last hash parameter is deprecated",
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
if (name) {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method `%"PRIsVALUE"' is defined here", name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method is defined here");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
rb_warn_split_last_hash_to_keyword(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
|
|
||||||
{
|
|
||||||
if (rb_warn_check(ec, iseq)) return;
|
|
||||||
|
|
||||||
VALUE name, loc;
|
|
||||||
name = rb_id2str(ci->mid);
|
|
||||||
loc = rb_iseq_location(iseq);
|
|
||||||
if (NIL_P(loc)) {
|
|
||||||
rb_warn("Splitting the last argument for `%"PRIsVALUE"' into positional and keyword parameters is deprecated",
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_warn("Splitting the last argument into positional and keyword parameters is deprecated");
|
|
||||||
if (calling->recv != Qundef) {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method `%"PRIsVALUE"' is defined here", name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method is defined here");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
rb_warn_last_hash_to_keyword(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
|
|
||||||
{
|
|
||||||
if (rb_warn_check(ec, iseq)) return;
|
|
||||||
|
|
||||||
VALUE name, loc;
|
|
||||||
name = rb_id2str(ci->mid);
|
|
||||||
loc = rb_iseq_location(iseq);
|
|
||||||
if (NIL_P(loc)) {
|
|
||||||
rb_warn("Using the last argument for `%"PRIsVALUE"' as keyword parameters is deprecated; maybe ** should be added to the call",
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_warn("Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call");
|
|
||||||
if (calling->recv != Qundef) {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method `%"PRIsVALUE"' is defined here", name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_compile_warn(RSTRING_PTR(RARRAY_AREF(loc, 0)), FIX2INT(RARRAY_AREF(loc, 1)),
|
|
||||||
"The called method is defined here");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -727,7 +470,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
||||||
const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS;
|
const int max_argc = (iseq->body->param.flags.has_rest == FALSE) ? min_argc + iseq->body->param.opt_num : UNLIMITED_ARGUMENTS;
|
||||||
int opt_pc = 0;
|
int opt_pc = 0;
|
||||||
int given_argc;
|
int given_argc;
|
||||||
int kw_splat = FALSE;
|
|
||||||
unsigned int kw_flag = ci->flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT);
|
unsigned int kw_flag = ci->flag & (VM_CALL_KWARG | VM_CALL_KW_SPLAT);
|
||||||
struct args_info args_body, *args;
|
struct args_info args_body, *args;
|
||||||
VALUE keyword_hash = Qnil;
|
VALUE keyword_hash = Qnil;
|
||||||
|
@ -813,46 +555,47 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
||||||
|
|
||||||
if (kw_flag & VM_CALL_KW_SPLAT) {
|
if (kw_flag & VM_CALL_KW_SPLAT) {
|
||||||
if (len > 0 && ignore_keyword_hash_p(rest_last, iseq)) {
|
if (len > 0 && ignore_keyword_hash_p(rest_last, iseq)) {
|
||||||
if (given_argc != min_argc) {
|
if (remove_empty_keyword_hash) {
|
||||||
if (remove_empty_keyword_hash) {
|
arg_rest_dup(args);
|
||||||
arg_rest_dup(args);
|
rb_ary_pop(args->rest);
|
||||||
rb_ary_pop(args->rest);
|
given_argc--;
|
||||||
given_argc--;
|
kw_flag &= ~VM_CALL_KW_SPLAT;
|
||||||
kw_flag &= ~VM_CALL_KW_SPLAT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
flag_keyword_hash = rest_last;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
|
flag_keyword_hash = rest_last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!remove_empty_keyword_hash && rest_last) {
|
else if (!remove_empty_keyword_hash && rest_last) {
|
||||||
flag_keyword_hash = rest_last;
|
flag_keyword_hash = rest_last;
|
||||||
}
|
}
|
||||||
|
else if (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) {
|
||||||
|
arg_rest_dup(args);
|
||||||
|
rb_ary_pop(args->rest);
|
||||||
|
given_argc--;
|
||||||
|
keyword_hash = rest_last;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (kw_flag & VM_CALL_KW_SPLAT) {
|
if (kw_flag & VM_CALL_KW_SPLAT) {
|
||||||
VALUE last_arg = args->argv[args->argc-1];
|
VALUE last_arg = args->argv[args->argc-1];
|
||||||
if (ignore_keyword_hash_p(last_arg, iseq)) {
|
if (ignore_keyword_hash_p(last_arg, iseq)) {
|
||||||
if (given_argc != min_argc) {
|
if (remove_empty_keyword_hash) {
|
||||||
if (remove_empty_keyword_hash) {
|
args->argc--;
|
||||||
args->argc--;
|
given_argc--;
|
||||||
given_argc--;
|
kw_flag &= ~VM_CALL_KW_SPLAT;
|
||||||
kw_flag &= ~VM_CALL_KW_SPLAT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
flag_keyword_hash = last_arg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
|
flag_keyword_hash = last_arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!remove_empty_keyword_hash) {
|
else if (!remove_empty_keyword_hash) {
|
||||||
flag_keyword_hash = args->argv[args->argc-1];
|
flag_keyword_hash = last_arg;
|
||||||
|
}
|
||||||
|
else if (iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) {
|
||||||
|
args->argc--;
|
||||||
|
given_argc--;
|
||||||
|
keyword_hash = last_arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args->rest = Qfalse;
|
args->rest = Qfalse;
|
||||||
|
@ -870,7 +613,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
||||||
case arg_setup_method:
|
case arg_setup_method:
|
||||||
break; /* do nothing special */
|
break; /* do nothing special */
|
||||||
case arg_setup_block:
|
case arg_setup_block:
|
||||||
if (given_argc == 1 &&
|
if (given_argc == (keyword_hash == Qnil ? 1 : 2) &&
|
||||||
(min_argc > 0 || iseq->body->param.opt_num > 1 ||
|
(min_argc > 0 || iseq->body->param.opt_num > 1 ||
|
||||||
iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
|
iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest) &&
|
||||||
!iseq->body->param.flags.ambiguous_param0 &&
|
!iseq->body->param.flags.ambiguous_param0 &&
|
||||||
|
@ -882,60 +625,13 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
||||||
|
|
||||||
/* argc check */
|
/* argc check */
|
||||||
if (given_argc < min_argc) {
|
if (given_argc < min_argc) {
|
||||||
if (given_argc == min_argc - 1 && args->kw_argv) {
|
if (arg_setup_type == arg_setup_block) {
|
||||||
args_stored_kw_argv_to_hash(args);
|
CHECK_VM_STACK_OVERFLOW(ec->cfp, min_argc);
|
||||||
given_argc = args_argc(args);
|
given_argc = min_argc;
|
||||||
}
|
args_extend(args, min_argc);
|
||||||
else {
|
|
||||||
if (arg_setup_type == arg_setup_block) {
|
|
||||||
CHECK_VM_STACK_OVERFLOW(ec->cfp, min_argc);
|
|
||||||
given_argc = min_argc;
|
|
||||||
args_extend(args, min_argc);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
argument_arity_error(ec, iseq, given_argc, min_argc, max_argc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kw_flag & VM_CALL_KW_SPLAT) {
|
|
||||||
kw_splat = !iseq->body->param.flags.has_rest;
|
|
||||||
}
|
|
||||||
if ((iseq->body->param.flags.has_kw || iseq->body->param.flags.has_kwrest ||
|
|
||||||
(kw_splat && given_argc > max_argc)) &&
|
|
||||||
args->kw_argv == NULL) {
|
|
||||||
if (given_argc > min_argc) {
|
|
||||||
if (kw_flag) {
|
|
||||||
int check_only_symbol = (kw_flag & VM_CALL_KW_SPLAT) &&
|
|
||||||
iseq->body->param.flags.has_kw &&
|
|
||||||
!iseq->body->param.flags.has_kwrest;
|
|
||||||
|
|
||||||
if (args_pop_keyword_hash(args, &keyword_hash, check_only_symbol)) {
|
|
||||||
given_argc--;
|
|
||||||
}
|
|
||||||
else if (check_only_symbol) {
|
|
||||||
if (keyword_hash != Qnil) {
|
|
||||||
rb_warn_split_last_hash_to_keyword(ec, calling, ci, iseq);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (args_pop_keyword_hash(args, &keyword_hash, 1)) {
|
|
||||||
/* Warn the following:
|
|
||||||
* def foo(k:1) p [k]; end
|
|
||||||
* foo({k:42}) #=> 42
|
|
||||||
*/
|
|
||||||
rb_warn_last_hash_to_keyword(ec, calling, ci, iseq);
|
|
||||||
given_argc--;
|
|
||||||
}
|
|
||||||
else if (keyword_hash != Qnil) {
|
|
||||||
rb_warn_split_last_hash_to_keyword(ec, calling, ci, iseq);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (given_argc == min_argc && kw_flag) {
|
else {
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
|
argument_arity_error(ec, iseq, given_argc, min_argc, max_argc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1160,8 +856,6 @@ refine_sym_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg))
|
||||||
const VALUE symbol = RARRAY_AREF(callback_arg, 0);
|
const VALUE symbol = RARRAY_AREF(callback_arg, 0);
|
||||||
const VALUE refinements = RARRAY_AREF(callback_arg, 1);
|
const VALUE refinements = RARRAY_AREF(callback_arg, 1);
|
||||||
int kw_splat = RB_PASS_CALLED_KEYWORDS;
|
int kw_splat = RB_PASS_CALLED_KEYWORDS;
|
||||||
VALUE v;
|
|
||||||
VALUE ret;
|
|
||||||
VALUE klass;
|
VALUE klass;
|
||||||
|
|
||||||
if (argc-- < 1) {
|
if (argc-- < 1) {
|
||||||
|
@ -1182,15 +876,10 @@ refine_sym_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg))
|
||||||
if (!NIL_P(blockarg)) {
|
if (!NIL_P(blockarg)) {
|
||||||
vm_passed_block_handler_set(ec, blockarg);
|
vm_passed_block_handler_set(ec, blockarg);
|
||||||
}
|
}
|
||||||
v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
if (!me) {
|
if (!me) {
|
||||||
ret = method_missing(obj, mid, argc, argv, MISSING_NOENTRY, kw_splat);
|
return method_missing(obj, mid, argc, argv, MISSING_NOENTRY, kw_splat);
|
||||||
}
|
}
|
||||||
else {
|
return rb_vm_call0(ec, obj, mid, argc, argv, me, kw_splat);
|
||||||
ret = rb_vm_call0(ec, obj, mid, argc, argv, me, kw_splat);
|
|
||||||
}
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
|
12
vm_core.h
12
vm_core.h
|
@ -1159,11 +1159,11 @@ typedef rb_control_frame_t *
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* Frame/Environment flag bits:
|
/* Frame/Environment flag bits:
|
||||||
* MMMM MMMM MMMM MMMM ____ FFFF FFFF EEEX (LSB)
|
* MMMM MMMM MMMM MMMM ____ _FFF FFFF EEEX (LSB)
|
||||||
*
|
*
|
||||||
* X : tag for GC marking (It seems as Fixnum)
|
* X : tag for GC marking (It seems as Fixnum)
|
||||||
* EEE : 3 bits Env flags
|
* EEE : 3 bits Env flags
|
||||||
* FF..: 8 bits Frame flags
|
* FF..: 7 bits Frame flags
|
||||||
* MM..: 15 bits frame magic (to check frame corruption)
|
* MM..: 15 bits frame magic (to check frame corruption)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1188,7 +1188,6 @@ enum {
|
||||||
VM_FRAME_FLAG_LAMBDA = 0x0100,
|
VM_FRAME_FLAG_LAMBDA = 0x0100,
|
||||||
VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200,
|
VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200,
|
||||||
VM_FRAME_FLAG_CFRAME_KW = 0x0400,
|
VM_FRAME_FLAG_CFRAME_KW = 0x0400,
|
||||||
VM_FRAME_FLAG_CFRAME_EMPTY_KW = 0x0800, /* -- Remove In 3.0 -- */
|
|
||||||
|
|
||||||
/* env flag */
|
/* env flag */
|
||||||
VM_ENV_FLAG_LOCAL = 0x0002,
|
VM_ENV_FLAG_LOCAL = 0x0002,
|
||||||
|
@ -1249,13 +1248,6 @@ VM_FRAME_CFRAME_KW_P(const rb_control_frame_t *cfp)
|
||||||
return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_CFRAME_KW) != 0;
|
return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_CFRAME_KW) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Remove In 3.0 -- */
|
|
||||||
static inline int
|
|
||||||
VM_FRAME_CFRAME_EMPTY_KW_P(const rb_control_frame_t *cfp)
|
|
||||||
{
|
|
||||||
return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_CFRAME_EMPTY_KW) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
VM_FRAME_FINISHED_P(const rb_control_frame_t *cfp)
|
VM_FRAME_FINISHED_P(const rb_control_frame_t *cfp)
|
||||||
{
|
{
|
||||||
|
|
106
vm_eval.c
106
vm_eval.c
|
@ -69,7 +69,6 @@ vm_call0_cfunc_with_frame(rb_execution_context_t* ec, struct rb_calling_info *ca
|
||||||
|
|
||||||
if (calling->kw_splat) {
|
if (calling->kw_splat) {
|
||||||
if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH) && RHASH_EMPTY_P(argv[argc-1])) {
|
if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH) && RHASH_EMPTY_P(argv[argc-1])) {
|
||||||
frame_flags |= VM_FRAME_FLAG_CFRAME_EMPTY_KW;
|
|
||||||
argc--;
|
argc--;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -144,12 +143,7 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, struc
|
||||||
calling->argc > 0 &&
|
calling->argc > 0 &&
|
||||||
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
|
RB_TYPE_P(argv[calling->argc-1], T_HASH) &&
|
||||||
RHASH_EMPTY_P(argv[calling->argc-1])) {
|
RHASH_EMPTY_P(argv[calling->argc-1])) {
|
||||||
if (calling->argc == 1) {
|
calling->argc--;
|
||||||
rb_warn("Passing the keyword argument as the last hash parameter is deprecated");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
calling->argc--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rb_check_arity(calling->argc, 1, 1);
|
rb_check_arity(calling->argc, 1, 1);
|
||||||
|
@ -232,42 +226,10 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, struc
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller should keep the reference to the return value until argv becomes useless. */
|
|
||||||
MJIT_FUNC_EXPORTED VALUE
|
|
||||||
rb_adjust_argv_kw_splat(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 v;
|
|
||||||
VALUE *ptr = rb_alloc_tmp_buffer2(&v, n+1, sizeof(VALUE));
|
|
||||||
if (n) memcpy(ptr, *argv, sizeof(VALUE)*n);
|
|
||||||
ptr[n] = rb_hash_new();
|
|
||||||
*argc = ++n;
|
|
||||||
*argv = ptr;
|
|
||||||
*kw_splat = 1;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*kw_splat = rb_keyword_given_p();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*kw_splat && (*argc == 0 || !RB_TYPE_P((*argv)[(*argc)-1], T_HASH))) {
|
|
||||||
rb_warn("Keyword flag passed calling internal method, but last entry is not a hash, unsetting keyword flag");
|
|
||||||
*kw_splat = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MJIT_FUNC_EXPORTED VALUE
|
MJIT_FUNC_EXPORTED 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)
|
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)
|
||||||
{
|
{
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
return rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat);
|
||||||
VALUE ret = rb_vm_call0(ec, recv, id, argc, argv, me, kw_splat);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
|
@ -961,10 +923,7 @@ rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
|
||||||
VALUE
|
VALUE
|
||||||
rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
||||||
{
|
{
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
|
||||||
VALUE ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_FCALL_KW : CALL_FCALL);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -985,10 +944,7 @@ rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
|
||||||
VALUE
|
VALUE
|
||||||
rb_funcallv_public_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
rb_funcallv_public_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
||||||
{
|
{
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
||||||
VALUE ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1038,12 +994,8 @@ rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
|
||||||
VALUE
|
VALUE
|
||||||
rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
|
||||||
{
|
{
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
VALUE ret;
|
|
||||||
PASS_PASSED_BLOCK_HANDLER();
|
PASS_PASSED_BLOCK_HANDLER();
|
||||||
ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
|
@ -1063,10 +1015,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);
|
vm_passed_block_handler_set(GET_EC(), passed_procval);
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
return rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
||||||
VALUE ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE *
|
static VALUE *
|
||||||
|
@ -1140,10 +1089,7 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
||||||
static VALUE
|
static VALUE
|
||||||
send_internal_kw(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
send_internal_kw(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
||||||
{
|
{
|
||||||
VALUE v=0, ret;
|
if (rb_keyword_given_p()) {
|
||||||
int kw_splat = RB_PASS_CALLED_KEYWORDS;
|
|
||||||
v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
if (kw_splat) {
|
|
||||||
switch (scope) {
|
switch (scope) {
|
||||||
case CALL_PUBLIC:
|
case CALL_PUBLIC:
|
||||||
scope = CALL_PUBLIC_KW;
|
scope = CALL_PUBLIC_KW;
|
||||||
|
@ -1155,9 +1101,7 @@ send_internal_kw(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = send_internal(argc, argv, recv, scope);
|
return send_internal(argc, argv, recv, scope);
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1215,10 +1159,7 @@ rb_f_public_send(int argc, VALUE *argv, VALUE recv)
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
rb_yield_0_kw(int argc, const VALUE * argv, int kw_splat)
|
rb_yield_0_kw(int argc, const VALUE * argv, int kw_splat)
|
||||||
{
|
{
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
return vm_yield(GET_EC(), argc, argv, kw_splat);
|
||||||
VALUE ret = vm_yield(GET_EC(), argc, argv, kw_splat);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
|
@ -1314,13 +1255,9 @@ rb_yield_force_blockarg(VALUE values)
|
||||||
VALUE
|
VALUE
|
||||||
rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(val, arg))
|
rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(val, arg))
|
||||||
{
|
{
|
||||||
int kw_splat = RB_PASS_CALLED_KEYWORDS;
|
return vm_yield_with_block(GET_EC(), argc, argv,
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
NIL_P(blockarg) ? VM_BLOCK_HANDLER_NONE : blockarg,
|
||||||
VALUE ret = vm_yield_with_block(GET_EC(), argc, argv,
|
rb_keyword_given_p());
|
||||||
NIL_P(blockarg) ? VM_BLOCK_HANDLER_NONE : blockarg,
|
|
||||||
kw_splat);
|
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -1486,15 +1423,12 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv,
|
||||||
{
|
{
|
||||||
struct iter_method_arg arg;
|
struct iter_method_arg arg;
|
||||||
|
|
||||||
VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
arg.obj = obj;
|
arg.obj = obj;
|
||||||
arg.mid = mid;
|
arg.mid = mid;
|
||||||
arg.argc = argc;
|
arg.argc = argc;
|
||||||
arg.argv = argv;
|
arg.argv = argv;
|
||||||
arg.kw_splat = kw_splat;
|
arg.kw_splat = kw_splat;
|
||||||
VALUE ret = rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
|
return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
|
@ -1839,9 +1773,6 @@ yield_under(VALUE under, VALUE self, int argc, const VALUE *argv, int kw_splat)
|
||||||
const VALUE *ep = NULL;
|
const VALUE *ep = NULL;
|
||||||
rb_cref_t *cref;
|
rb_cref_t *cref;
|
||||||
int is_lambda = FALSE;
|
int is_lambda = FALSE;
|
||||||
VALUE v = 0, ret;
|
|
||||||
|
|
||||||
v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
|
||||||
|
|
||||||
if (block_handler != VM_BLOCK_HANDLER_NONE) {
|
if (block_handler != VM_BLOCK_HANDLER_NONE) {
|
||||||
again:
|
again:
|
||||||
|
@ -1861,10 +1792,9 @@ yield_under(VALUE under, VALUE self, int argc, const VALUE *argv, int kw_splat)
|
||||||
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
|
block_handler = vm_proc_to_block_handler(VM_BH_TO_PROC(block_handler));
|
||||||
goto again;
|
goto again;
|
||||||
case block_handler_type_symbol:
|
case block_handler_type_symbol:
|
||||||
ret = rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)),
|
return rb_sym_proc_call(SYM2ID(VM_BH_TO_SYMBOL(block_handler)),
|
||||||
argc, argv, kw_splat, VM_BLOCK_HANDLER_NONE);
|
argc, argv, kw_splat,
|
||||||
rb_free_tmp_buffer(&v);
|
VM_BLOCK_HANDLER_NONE);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_captured.self = self;
|
new_captured.self = self;
|
||||||
|
@ -1874,9 +1804,7 @@ yield_under(VALUE under, VALUE self, int argc, const VALUE *argv, int kw_splat)
|
||||||
}
|
}
|
||||||
|
|
||||||
cref = vm_cref_push(ec, under, ep, TRUE);
|
cref = vm_cref_push(ec, under, ep, TRUE);
|
||||||
ret = vm_yield_with_cref(ec, argc, argv, kw_splat, cref, is_lambda);
|
return vm_yield_with_cref(ec, argc, argv, kw_splat, cref, is_lambda);
|
||||||
rb_free_tmp_buffer(&v);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
|
|
|
@ -2486,9 +2486,8 @@ vm_method_cfunc_entry(const rb_callable_method_entry_t *me)
|
||||||
return UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
|
return UNALIGNED_MEMBER_PTR(me->def, body.cfunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Remove empty_kw_splat In 3.0 -- */
|
|
||||||
static VALUE
|
static VALUE
|
||||||
vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat)
|
vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd)
|
||||||
{
|
{
|
||||||
const struct rb_call_info *ci = &cd->ci;
|
const struct rb_call_info *ci = &cd->ci;
|
||||||
const struct rb_call_cache *cc = &cd->cc;
|
const struct rb_call_cache *cc = &cd->cc;
|
||||||
|
@ -2506,9 +2505,6 @@ vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp
|
||||||
if (UNLIKELY(calling->kw_splat)) {
|
if (UNLIKELY(calling->kw_splat)) {
|
||||||
frame_type |= VM_FRAME_FLAG_CFRAME_KW;
|
frame_type |= VM_FRAME_FLAG_CFRAME_KW;
|
||||||
}
|
}
|
||||||
else if (UNLIKELY(empty_kw_splat)) {
|
|
||||||
frame_type |= VM_FRAME_FLAG_CFRAME_EMPTY_KW;
|
|
||||||
}
|
|
||||||
|
|
||||||
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
|
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
|
||||||
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);
|
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);
|
||||||
|
@ -2536,16 +2532,11 @@ static VALUE
|
||||||
vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd)
|
vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd)
|
||||||
{
|
{
|
||||||
const struct rb_call_info *ci = &cd->ci;
|
const struct rb_call_info *ci = &cd->ci;
|
||||||
int empty_kw_splat;
|
|
||||||
RB_DEBUG_COUNTER_INC(ccf_cfunc);
|
RB_DEBUG_COUNTER_INC(ccf_cfunc);
|
||||||
|
|
||||||
CALLER_SETUP_ARG(reg_cfp, calling, ci);
|
CALLER_SETUP_ARG(reg_cfp, calling, ci);
|
||||||
empty_kw_splat = calling->kw_splat;
|
|
||||||
CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci);
|
CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci);
|
||||||
if (empty_kw_splat && calling->kw_splat) {
|
return vm_call_cfunc_with_frame(ec, reg_cfp, calling, cd);
|
||||||
empty_kw_splat = 0;
|
|
||||||
}
|
|
||||||
return vm_call_cfunc_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -2935,12 +2926,7 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st
|
||||||
|
|
||||||
case VM_METHOD_TYPE_ATTRSET:
|
case VM_METHOD_TYPE_ATTRSET:
|
||||||
CALLER_SETUP_ARG(cfp, calling, ci);
|
CALLER_SETUP_ARG(cfp, calling, ci);
|
||||||
if (calling->argc == 1 && calling->kw_splat && RHASH_EMPTY_P(cfp->sp[-1])) {
|
CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, NULL);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
|
|
||||||
}
|
|
||||||
|
|
||||||
rb_check_arity(calling->argc, 1, 1);
|
rb_check_arity(calling->argc, 1, 1);
|
||||||
cc->aux.index = 0;
|
cc->aux.index = 0;
|
||||||
|
@ -3211,13 +3197,8 @@ vm_yield_with_cfunc(rb_execution_context_t *ec,
|
||||||
blockarg = rb_vm_bh_to_procval(ec, block_handler);
|
blockarg = rb_vm_bh_to_procval(ec, block_handler);
|
||||||
|
|
||||||
frame_flag = VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME | (me ? VM_FRAME_FLAG_BMETHOD : 0);
|
frame_flag = VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME | (me ? VM_FRAME_FLAG_BMETHOD : 0);
|
||||||
switch (kw_splat) {
|
if (kw_splat) {
|
||||||
case 1:
|
frame_flag |= VM_FRAME_FLAG_CFRAME_KW;
|
||||||
frame_flag |= VM_FRAME_FLAG_CFRAME_KW;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
frame_flag |= VM_FRAME_FLAG_CFRAME_EMPTY_KW;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_push_frame(ec, (const rb_iseq_t *)captured->code.ifunc,
|
vm_push_frame(ec, (const rb_iseq_t *)captured->code.ifunc,
|
||||||
|
@ -3274,12 +3255,7 @@ vm_callee_setup_block_arg(rb_execution_context_t *ec, struct rb_calling_info *ca
|
||||||
VALUE arg0;
|
VALUE arg0;
|
||||||
|
|
||||||
CALLER_SETUP_ARG(cfp, calling, ci);
|
CALLER_SETUP_ARG(cfp, calling, ci);
|
||||||
if (calling->kw_splat && calling->argc == iseq->body->param.lead_num + iseq->body->param.post_num && RHASH_EMPTY_P(cfp->sp[-1])) {
|
CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
|
||||||
rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg_setup_type == arg_setup_block &&
|
if (arg_setup_type == arg_setup_block &&
|
||||||
calling->argc == 1 &&
|
calling->argc == 1 &&
|
||||||
|
@ -3377,17 +3353,10 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
|
||||||
{
|
{
|
||||||
VALUE val;
|
VALUE val;
|
||||||
int argc;
|
int argc;
|
||||||
int kw_splat = calling->kw_splat;
|
|
||||||
CALLER_SETUP_ARG(ec->cfp, calling, ci);
|
CALLER_SETUP_ARG(ec->cfp, calling, ci);
|
||||||
CALLER_REMOVE_EMPTY_KW_SPLAT(ec->cfp, calling, ci);
|
CALLER_REMOVE_EMPTY_KW_SPLAT(ec->cfp, calling, ci);
|
||||||
if (kw_splat && !calling->kw_splat) {
|
|
||||||
kw_splat = 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
kw_splat = calling->kw_splat;
|
|
||||||
}
|
|
||||||
argc = calling->argc;
|
argc = calling->argc;
|
||||||
val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), kw_splat, calling->block_handler, NULL);
|
val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), calling->kw_splat, calling->block_handler, NULL);
|
||||||
POPN(argc); /* TODO: should put before C/yield? */
|
POPN(argc); /* TODO: should put before C/yield? */
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue