From 568472906b178607ee569bff0d3074c27515719e Mon Sep 17 00:00:00 2001 From: nagachika Date: Fri, 4 Aug 2017 14:31:52 +0000 Subject: merge revision(s) 59357,59358: [Backport #13391] [Backport #13404] proc.c: rb_block_min_max_arity * proc.c (rb_block_min_max_arity): new function to get arity range from the current block. vm_eval.c: rb_lambda_call * enum.c (enum_collect): make the block arity same as the given block. [Bug #13391] * internal.h (vm_ifunc): store arity instead of unused id. * proc.c (rb_vm_block_min_max_arity): return ifunc arity. * vm_eval.c (rb_lambda_call): call method with lambda block. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_4@59500 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- enum.c | 4 ++- internal.h | 23 ++++++++++++++++-- proc.c | 66 +++++++++++++++++++++++++++++++++++++++++++------- test/ruby/test_enum.rb | 4 +++ version.h | 2 +- vm_eval.c | 19 ++++++++++++++- 6 files changed, 104 insertions(+), 14 deletions(-) diff --git a/enum.c b/enum.c index 67b3069d7e..5c0634d8d9 100644 --- a/enum.c +++ b/enum.c @@ -496,11 +496,13 @@ static VALUE enum_collect(VALUE obj) { VALUE ary; + int min_argc, max_argc; RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); ary = rb_ary_new(); - rb_block_call(obj, id_each, 0, 0, collect_i, ary); + min_argc = rb_block_min_max_arity(&max_argc); + rb_lambda_call(obj, id_each, 0, 0, collect_i, min_argc, max_argc, ary); return ary; } diff --git a/internal.h b/internal.h index d05d5366dd..a789fd887e 100644 --- a/internal.h +++ b/internal.h @@ -790,15 +790,30 @@ struct vm_throw_data { /* IFUNC */ +struct vm_ifunc_argc { +#if SIZEOF_INT * 2 > SIZEOF_VALUE + int min: (SIZEOF_VALUE * CHAR_BIT) / 2; + int max: (SIZEOF_VALUE * CHAR_BIT) / 2; +#else + int min, max; +#endif +}; + struct vm_ifunc { VALUE flags; VALUE reserved; VALUE (*func)(ANYARGS); const void *data; - ID id; + struct vm_ifunc_argc argc; }; #define IFUNC_NEW(a, b, c) ((struct vm_ifunc *)rb_imemo_new(imemo_ifunc, (VALUE)(a), (VALUE)(b), (VALUE)(c), 0)) +struct vm_ifunc *rb_vm_ifunc_new(VALUE (*func)(ANYARGS), const void *data, int min_argc, int max_argc); +static inline struct vm_ifunc * +rb_vm_ifunc_proc_new(VALUE (*func)(ANYARGS), const void *data) +{ + return rb_vm_ifunc_new(func, data, 0, UNLIMITED_ARGUMENTS); +} /* MEMO */ @@ -1335,8 +1350,9 @@ ID rb_id_attrget(ID id); VALUE rb_proc_location(VALUE self); st_index_t rb_hash_proc(st_index_t hash, VALUE proc); int rb_block_arity(void); +int rb_block_min_max_arity(int *max); VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val); -VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val); +VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc); /* process.c */ #define RB_MAX_GROUPS (65536) @@ -1596,6 +1612,9 @@ VALUE rb_check_funcall_default(VALUE, ID, int, const VALUE *, VALUE); VALUE rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, int *stateptr); VALUE rb_yield_1(VALUE val); VALUE rb_yield_lambda(VALUE values); +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); /* vm_insnhelper.c */ VALUE rb_equal_opt(VALUE obj1, VALUE obj2); diff --git a/proc.c b/proc.c index ba8242e01a..da8c763799 100644 --- a/proc.c +++ b/proc.c @@ -635,16 +635,47 @@ sym_proc_new(VALUE klass, VALUE sym) return procval; } +struct vm_ifunc * +rb_vm_ifunc_new(VALUE (*func)(ANYARGS), const void *data, int min_argc, int max_argc) +{ + union { + struct vm_ifunc_argc argc; + VALUE packed; + } arity; + + if (min_argc < UNLIMITED_ARGUMENTS || +#if SIZEOF_INT * 2 > SIZEOF_VALUE + min_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || +#endif + 0) { + rb_raise(rb_eRangeError, "minimum argument number out of range: %d", + min_argc); + } + if (max_argc < UNLIMITED_ARGUMENTS || +#if SIZEOF_INT * 2 > SIZEOF_VALUE + max_argc >= (int)(1U << (SIZEOF_VALUE * CHAR_BIT) / 2) || +#endif + 0) { + rb_raise(rb_eRangeError, "maximum argument number out of range: %d", + max_argc); + } + arity.argc.min = min_argc; + arity.argc.max = max_argc; + return IFUNC_NEW(func, data, arity.packed); +} + VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val) { - return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 0); + struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(func, (void *)val); + return cfunc_proc_new(rb_cProc, (VALUE)ifunc, 0); } VALUE -rb_func_lambda_new(rb_block_call_func_t func, VALUE val) +rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc) { - return cfunc_proc_new(rb_cProc, (VALUE)IFUNC_NEW(func, val, 0), 1); + struct vm_ifunc *ifunc = rb_vm_ifunc_new(func, (void *)val, min_argc, max_argc); + return cfunc_proc_new(rb_cProc, (VALUE)ifunc, 1); } static const char proc_without_block[] = "tried to create Proc object without a block"; @@ -928,13 +959,13 @@ rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max) } static int -rb_block_min_max_arity(const struct rb_block *block, int *max) +rb_vm_block_min_max_arity(const struct rb_block *block, int *max) { switch (vm_block_type(block)) { case block_type_iseq: return rb_iseq_min_max_arity(block->as.captured.code.iseq, max); case block_type_proc: - return rb_block_min_max_arity(vm_proc_block(block->as.proc), max); + return rb_vm_block_min_max_arity(vm_proc_block(block->as.proc), max); case block_type_ifunc: { const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; @@ -942,8 +973,9 @@ rb_block_min_max_arity(const struct rb_block *block, int *max) /* e.g. method(:foo).to_proc.arity */ return method_min_max_arity((VALUE)ifunc->data, max); } + *max = ifunc->argc.max; + return ifunc->argc.min; } - /* fall through */ case block_type_symbol: break; } @@ -962,7 +994,7 @@ rb_proc_min_max_arity(VALUE self, int *max) { rb_proc_t *proc; GetProcPtr(self, proc); - return rb_block_min_max_arity(&proc->block, max); + return rb_vm_block_min_max_arity(&proc->block, max); } int @@ -971,7 +1003,7 @@ rb_proc_arity(VALUE self) rb_proc_t *proc; int max, min; GetProcPtr(self, proc); - min = rb_block_min_max_arity(&proc->block, &max); + min = rb_vm_block_min_max_arity(&proc->block, &max); return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; } @@ -1011,7 +1043,7 @@ rb_block_arity(void) } block_setup(&block, block_handler); - min = rb_block_min_max_arity(&block, &max); + min = rb_vm_block_min_max_arity(&block, &max); switch (vm_block_type(&block)) { case block_handler_type_symbol: @@ -1031,6 +1063,22 @@ rb_block_arity(void) } } +int +rb_block_min_max_arity(int *max) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + VALUE block_handler = rb_vm_frame_block_handler(cfp); + struct rb_block block; + + if (block_handler == VM_BLOCK_HANDLER_NONE) { + rb_raise(rb_eArgError, "no block given"); + } + + block_setup(&block, block_handler); + return rb_vm_block_min_max_arity(&block, max); +} + const rb_iseq_t * rb_proc_get_iseq(VALUE self, int *is_proc) { diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index c70ec7ceba..51efac5150 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -798,6 +798,10 @@ class TestEnumerable < Test::Unit::TestCase lambda2 = ->(x, i) { [x.upcase, i] } assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]], @obj.each_with_index.map(&lambda2)) + + hash = { a: 'hoge', b: 'fuga' } + lambda = -> (k, v) { "#{k}:#{v}" } + assert_equal ["a:hoge", "b:fuga"], hash.map(&lambda) end def test_flat_map diff --git a/version.h b/version.h index ff51b968aa..50df49824e 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ #define RUBY_VERSION "2.4.2" #define RUBY_RELEASE_DATE "2017-08-04" -#define RUBY_PATCHLEVEL 167 +#define RUBY_PATCHLEVEL 168 #define RUBY_RELEASE_YEAR 2017 #define RUBY_RELEASE_MONTH 8 diff --git a/vm_eval.c b/vm_eval.c index d820f628b5..d4c3449bff 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1203,7 +1203,7 @@ rb_iterate(VALUE (* it_proc)(VALUE), VALUE data1, VALUE (* bl_proc)(ANYARGS), VALUE data2) { return rb_iterate0(it_proc, data1, - bl_proc ? IFUNC_NEW(bl_proc, data2, rb_frame_this_func()) : 0, + bl_proc ? rb_vm_ifunc_proc_new(bl_proc, (void *)data2) : 0, GET_THREAD()); } @@ -1236,6 +1236,23 @@ rb_block_call(VALUE obj, ID mid, int argc, const VALUE * argv, return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2); } +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) +{ + struct iter_method_arg arg; + struct vm_ifunc *block; + + if (!bl_proc) rb_raise(rb_eArgError, "NULL lambda function"); + arg.obj = obj; + arg.mid = mid; + arg.argc = argc; + arg.argv = argv; + block = rb_vm_ifunc_new(bl_proc, (void *)data2, min_argc, max_argc); + return rb_iterate0(iterate_method, (VALUE)&arg, block, GET_THREAD()); +} + static VALUE iterate_check_method(VALUE obj) { -- cgit v1.2.3