summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2021-10-06 13:55:02 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2021-10-07 15:39:47 -0700
commit217df51f0e5d9824ed712a4d175f555d932e44d8 (patch)
treecc3687806ac84c8d6d239d9bbde6255804e99f07
parentcb5a3b198e2a196c701d82842b3eb06ed0527110 (diff)
Dump outer variables tables when dumping an iseq to binary
This commit dumps the outer variables table when dumping an iseq to binary. This fixes a case where Ractors aren't able to tell what outer variables belong to a lambda after the lambda is loaded via ISeq.load_from_binary [Bug #18232] [ruby-core:105504]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/4942
-rw-r--r--compile.c55
-rw-r--r--test/ruby/test_iseq.rb10
2 files changed, 64 insertions, 1 deletions
diff --git a/compile.c b/compile.c
index 9d34ccbf05..e9295a47b4 100644
--- a/compile.c
+++ b/compile.c
@@ -10622,7 +10622,7 @@ typedef unsigned int ibf_offset_t;
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
#if RUBY_DEVEL
-#define IBF_DEVEL_VERSION 2
+#define IBF_DEVEL_VERSION 3
#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
#else
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
@@ -11480,6 +11480,33 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
return offset;
}
+static enum rb_id_table_iterator_result
+dump_outer_variable(ID id, VALUE val, void *dump)
+{
+ ibf_dump_write_small_value(dump, ibf_dump_id(dump, id));
+ ibf_dump_write_small_value(dump, val);
+
+ return ID_TABLE_CONTINUE;
+}
+
+static ibf_offset_t
+ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq)
+{
+ struct rb_id_table * ovs = iseq->body->outer_variables;
+
+ ibf_offset_t offset = ibf_dump_pos(dump);
+
+ if (ovs) {
+ ibf_dump_write_small_value(dump, (VALUE)rb_id_table_size(ovs));
+ rb_id_table_foreach(ovs, dump_outer_variable, (void *)dump);
+ }
+ else {
+ ibf_dump_write_small_value(dump, (VALUE)0);
+ }
+
+ return offset;
+}
+
/* note that we dump out rb_call_info but load back rb_call_data */
static void
ibf_load_ci_entries(const struct ibf_load *load,
@@ -11524,6 +11551,28 @@ ibf_load_ci_entries(const struct ibf_load *load,
}
}
+static struct rb_id_table *
+ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variables_offset)
+{
+ ibf_offset_t reading_pos = outer_variables_offset;
+
+ struct rb_id_table *tbl = NULL;
+
+ size_t table_size = (size_t)ibf_load_small_value(load, &reading_pos);
+
+ if (table_size > 0) {
+ tbl = rb_id_table_create(table_size);
+ }
+
+ for (size_t i = 0; i < table_size; i++) {
+ ID key = ibf_load_id(load, (ID)ibf_load_small_value(load, &reading_pos));
+ VALUE value = ibf_load_small_value(load, &reading_pos);
+ rb_id_table_insert(tbl, key, value);
+ }
+
+ return tbl;
+}
+
static ibf_offset_t
ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
@@ -11563,6 +11612,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
const int parent_iseq_index = ibf_dump_iseq(dump, iseq->body->parent_iseq);
const int local_iseq_index = ibf_dump_iseq(dump, iseq->body->local_iseq);
const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
+ const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
ibf_offset_t local_obj_list_offset;
@@ -11624,6 +11674,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_dump_write_small_value(dump, parent_iseq_index);
ibf_dump_write_small_value(dump, local_iseq_index);
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
+ ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
ibf_dump_write_small_value(dump, body->variable.flip_count);
ibf_dump_write_small_value(dump, body->local_table_size);
ibf_dump_write_small_value(dump, body->is_size);
@@ -11730,6 +11781,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
+ const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
const unsigned int local_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
const unsigned int is_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
@@ -11779,6 +11831,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, is_size);
ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
+ load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset);
load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
load_body->param.flags.has_kw = (param_flags >> 4) & 1;
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index 0cde1fbb7a..edd131823e 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -95,6 +95,16 @@ class TestISeq < Test::Unit::TestCase
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
end
+ def test_lambda_with_ractor_roundtrip
+ iseq = compile(<<~EOF)
+ x = 42
+ y = lambda { x }
+ Ractor.make_shareable(y)
+ y.call
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
def test_disasm_encoding
src = "\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm