From d2b57366fd5052fc7e6d10f5f294988149b47a9b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 6 Oct 2021 13:55:02 -0700 Subject: [PATCH] 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] --- compile.c | 52 ++++++++++++++++++++++++++++++++++++++++++ test/ruby/test_iseq.rb | 10 ++++++++ 2 files changed, 62 insertions(+) diff --git a/compile.c b/compile.c index 9d34ccbf05..817ec5abd0 100644 --- a/compile.c +++ b/compile.c @@ -11480,6 +11480,32 @@ 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 +11550,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 +11611,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 +11673,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 +11780,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 +11830,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 @@ def test_cdhash_after_roundtrip 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 -- 2.30.2