From e5403bd137d57991d8788cd29bbb5916d75acb36 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 13 Feb 2025 21:12:33 -0800 Subject: [PATCH] merge revision(s) db02a6b3ab4cabbdf492c26dcb1929b4ef0370a1: [Backport #21103] [Bug #21103] Fix local variable index calculation with forwarding Forwarding argument is optimized not to packed when no other arguments and an internal object refers values before it. This size is decided at called time, calculate the local variable index from the fixed end point. --- proc.c | 8 ++-- test/ruby/test_method.rb | 100 +++++++++++++++++++++++++++++++++++++++ version.h | 2 +- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/proc.c b/proc.c index c0fcb20b95..7a3302cd26 100644 --- a/proc.c +++ b/proc.c @@ -414,11 +414,11 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid) } const rb_iseq_t *iseq = env->iseq; - unsigned int i; VM_ASSERT(rb_obj_is_iseq((VALUE)iseq)); - for (i=0; ilocal_table_size; i++) { + const unsigned int local_table_size = ISEQ_BODY(iseq)->local_table_size; + for (unsigned int i=0; ilocal_table[i] == lid) { if (ISEQ_BODY(iseq)->local_iseq == iseq && ISEQ_BODY(iseq)->param.flags.has_block && @@ -431,7 +431,9 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid) } *envp = env; - return &env->env[i]; + unsigned int last_lvar = env->env_size+VM_ENV_INDEX_LAST_LVAR + - 1 /* errinfo */; + return &env->env[last_lvar - (local_table_size - i)]; } } } diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 5d5d5aac02..a865f6100b 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -1439,6 +1439,46 @@ class TestMethod < Test::Unit::TestCase def foo a = b = c = a = b = c = 12345 end + + def binding_noarg + a = a = 12345 + binding + end + + def binding_one_arg(x) + a = a = 12345 + binding + end + + def binding_optargs(x, y=42) + a = a = 12345 + binding + end + + def binding_anyargs(*x) + a = a = 12345 + binding + end + + def binding_keywords(x: 42) + a = a = 12345 + binding + end + + def binding_anykeywords(**x) + a = a = 12345 + binding + end + + def binding_forwarding(...) + a = a = 12345 + binding + end + + def binding_forwarding1(x, ...) + a = a = 12345 + binding + end end def test_to_proc_binding @@ -1457,6 +1497,66 @@ class TestMethod < Test::Unit::TestCase assert_equal([:bar, :foo], b.local_variables.sort, bug11012) end + def test_method_binding + c = C.new + + b = c.binding_noarg + assert_equal(12345, b.local_variable_get(:a)) + + b = c.binding_one_arg(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + + b = c.binding_anyargs() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([], b.local_variable_get(:x)) + b = c.binding_anyargs(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([0], b.local_variable_get(:x)) + b = c.binding_anyargs(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([0, 1], b.local_variable_get(:x)) + + b = c.binding_optargs(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + assert_equal(42, b.local_variable_get(:y)) + b = c.binding_optargs(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + assert_equal(1, b.local_variable_get(:y)) + + b = c.binding_keywords() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(42, b.local_variable_get(:x)) + b = c.binding_keywords(x: 102) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(102, b.local_variable_get(:x)) + + b = c.binding_anykeywords() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal({}, b.local_variable_get(:x)) + b = c.binding_anykeywords(foo: 999) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal({foo: 999}, b.local_variable_get(:x)) + + b = c.binding_forwarding() + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(0) + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(foo: 42) + assert_equal(12345, b.local_variable_get(:a)) + + b = c.binding_forwarding1(987) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(987, b.local_variable_get(:x)) + b = c.binding_forwarding1(987, 654) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(987, b.local_variable_get(:x)) + end + MethodInMethodClass_Setup = -> do remove_const :MethodInMethodClass if defined? MethodInMethodClass diff --git a/version.h b/version.h index 1a3a464e58..2d56275a35 100644 --- a/version.h +++ b/version.h @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 22 +#define RUBY_PATCHLEVEL 23 #include "ruby/version.h" #include "ruby/internal/abi.h"