diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h index 1ed2bf6368..2dbc1ee7bc 100644 --- a/include/ruby/internal/scan_args.h +++ b/include/ruby/internal/scan_args.h @@ -75,7 +75,7 @@ * Pass keywords if current method is called with keywords, useful for argument * delegation */ -#define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p() +#define RB_PASS_CALLED_KEYWORDS !!rb_keyword_given_p() /** @} */ diff --git a/internal/imemo.h b/internal/imemo.h index 36c0776987..7420909a14 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -87,6 +87,7 @@ struct vm_ifunc { const void *data; struct vm_ifunc_argc argc; }; +#define IFUNC_YIELD_OPTIMIZABLE IMEMO_FL_USER0 struct rb_imemo_tmpbuf_struct { VALUE flags; diff --git a/internal/vm.h b/internal/vm.h index 74635e6ad8..b40e6ebc32 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -77,6 +77,8 @@ VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, int min_argc, int max_argc, VALUE data2); void rb_check_stack_overflow(void); +#define RB_BLOCK_NO_USE_PACKED_ARGS 2 +VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, VALUE data2, long flags); #if USE_YJIT /* vm_exec.c */ diff --git a/proc.c b/proc.c index 09b288fecd..fd1edb2bdc 100644 --- a/proc.c +++ b/proc.c @@ -1159,6 +1159,12 @@ rb_block_pair_yield_optimizable(void) return min > 1; } + case block_handler_type_ifunc: + { + const struct vm_ifunc *ifunc = block.as.captured.code.ifunc; + if (ifunc->flags & IFUNC_YIELD_OPTIMIZABLE) return 1; + } + default: return min > 1; } diff --git a/vm_eval.c b/vm_eval.c index 7840ecac4b..56236dabc7 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1558,6 +1558,37 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv, return rb_iterate_internal(iterate_method, (VALUE)&arg, bl_proc, data2); } +/* + * A flexible variant of rb_block_call and rb_block_call_kw. + * This function accepts flags: + * + * RB_NO_KEYWORDS, RB_PASS_KEYWORDS, RB_PASS_CALLED_KEYWORDS: + * Works as the same as rb_block_call_kw. + * + * RB_BLOCK_NO_USE_PACKED_ARGS: + * The given block ("bl_proc") does not use "yielded_arg" of rb_block_call_func_t. + * Instead, the block accesses the yielded arguments via "argc" and "argv". + * This flag allows the called method to yield arguments without allocating an Array. + */ +VALUE +rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, + rb_block_call_func_t bl_proc, VALUE data2, long flags) +{ + struct iter_method_arg arg; + + arg.obj = obj; + arg.mid = mid; + arg.argc = argc; + arg.argv = argv; + arg.kw_splat = flags & 1; + + struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(bl_proc, (void *)data2); + if (flags & RB_BLOCK_NO_USE_PACKED_ARGS) + ifunc->flags |= IFUNC_YIELD_OPTIMIZABLE; + + return rb_iterate0(iterate_method, (VALUE)&arg, ifunc, GET_EC()); +} + VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, int min_argc, int max_argc,