[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.
This commit is contained in:
Nobuyoshi Nakada 2025-02-01 22:03:47 +09:00
parent 71f402c5d5
commit db02a6b3ab
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2025-02-01 14:15:25 +00:00
2 changed files with 105 additions and 3 deletions

8
proc.c
View File

@ -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; i<ISEQ_BODY(iseq)->local_table_size; i++) {
const unsigned int local_table_size = ISEQ_BODY(iseq)->local_table_size;
for (unsigned int i=0; i<local_table_size; i++) {
if (ISEQ_BODY(iseq)->local_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)];
}
}
}

View File

@ -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