From a3cfaf25031853f97114ecc6dcca852a02d95867 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 4 May 2026 07:12:22 +0200 Subject: [PATCH 1/2] test_shapes.rb: replace hardcoded MANY_IVS constant Since more size pools have been added, this constant was outdated. --- shape.c | 1 + test/ruby/test_shapes.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/shape.c b/shape.c index cca88b13b0c334..9fe30e3851bd02 100644 --- a/shape.c +++ b/shape.c @@ -1634,6 +1634,7 @@ Init_shape(void) rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS)); + rb_define_const(rb_cShape, "SHAPE_MAX_EMBEDDED_CAPACITY", INT2NUM(rb_shape_tree.capacities[rb_shape_tree.heaps_count - 1])); rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t))); rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t))); rb_define_const(rb_cShape, "SHAPE_BUFFER_SIZE", INT2NUM(sizeof(rb_shape_t) * SHAPE_BUFFER_SIZE)); diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index b3333b166095fe..717ef48a1ff6e8 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -6,7 +6,7 @@ # These test the functionality of object shapes class TestShapes < Test::Unit::TestCase - MANY_IVS = 80 + MANY_IVS = RubyVM::Shape::SHAPE_MAX_EMBEDDED_CAPACITY + 1 class IVOrder def expected_ivs From 8bf6d7bfcb4ca46d2cfe73bdbce2fdb0652b1aae Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 29 Apr 2026 13:12:51 +0900 Subject: [PATCH 2/2] Fix coverage support for RubyVM::ISeq.compile [Bug #22018] ISeq returned by `RubyVM::InstructionSequene.load_iseq` weren't handled by the coverage module. --- iseq.c | 17 +++++---- test/coverage/test_coverage.rb | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/iseq.c b/iseq.c index b4e490c455f2e7..8be62cdd9b4c68 100644 --- a/iseq.c +++ b/iseq.c @@ -864,10 +864,6 @@ set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt) static rb_compile_option_t * set_compile_option_from_ast(rb_compile_option_t *option, const rb_ast_body_t *ast) { -#define SET_COMPILE_OPTION(o, a, mem) \ - ((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0)) - SET_COMPILE_OPTION(option, ast, coverage_enabled); -#undef SET_COMPILE_OPTION if (ast->frozen_string_literal >= 0) { option->frozen_string_literal = ast->frozen_string_literal; } @@ -1021,8 +1017,13 @@ rb_iseq_new_eval(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, } } + rb_compile_option_t option = COMPILE_OPTION_DEFAULT; + rb_ast_t *ast = rb_ruby_ast_data_get(ast_value); + if (ast->body.coverage_enabled >= 0) { + option.coverage_enabled = ast->body.coverage_enabled; + } return rb_iseq_new_with_opt(ast_value, name, path, realpath, first_lineno, - parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT, + parent, isolated_depth, ISEQ_TYPE_EVAL, &option, Qnil); } @@ -1209,7 +1210,7 @@ rb_iseq_load_iseq(VALUE fname) VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("load_iseq"), 1, &fname); if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) { - return iseqw_check(iseqv); + return iseqw_check(iseqv); } return NULL; @@ -1397,6 +1398,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V rb_exc_raise(GET_EC()->errinfo); } else { + iseq_new_setup_coverage(file, ast_line_count(ast_value)); iseq = rb_iseq_new_with_opt(ast_value, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, Qnil); @@ -1462,6 +1464,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V if (error == Qnil) { int error_state; + iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); iseq = pm_iseq_new_with_opt(&result.node, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, &error_state); pm_parse_result_free(&result); @@ -1853,6 +1856,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) parser = rb_parser_new(); rb_parser_set_context(parser, NULL, FALSE); ast_value = rb_parser_load_file(parser, file); + iseq_new_setup_coverage(file, ast_line_count(ast_value)); ast = rb_ruby_ast_data_get(ast_value); if (!ast->body.root) exc = GET_EC()->errinfo; @@ -1941,6 +1945,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self) if (error == Qnil) { int error_state; + iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); rb_iseq_t *iseq = pm_iseq_new_with_opt(&result.node, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 80f8930472af70..adcd4a946c5aae 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -82,6 +82,70 @@ def coverage_test_snapshot } end + def test_coverage_snapshot_iseq_compile + Dir.mktmpdir {|tmp| + Dir.chdir(tmp) { + File.open("test.rb", "w") do |f| + f.puts <<-EOS + def coverage_test_snapshot + :ok + end + EOS + end + + assert_in_out_err(ARGV, <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], []) + class RubyVM::InstructionSequence + def self.load_iseq(path) + compile(File.read(path), path, path) + end + end + + Coverage.start + tmp = Dir.pwd + require tmp + "/test.rb" + cov = Coverage.peek_result[tmp + "/test.rb"] + coverage_test_snapshot + cov2 = Coverage.peek_result[tmp + "/test.rb"] + p cov + p cov2 + p Coverage.result[tmp + "/test.rb"] + end; + } + } + end + + def test_coverage_snapshot_iseq_compile_file + Dir.mktmpdir {|tmp| + Dir.chdir(tmp) { + File.open("test.rb", "w") do |f| + f.puts <<-EOS + def coverage_test_snapshot + :ok + end + EOS + end + + assert_in_out_err(ARGV, <<-"end;", ["[1, 0, nil]", "[1, 1, nil]", "[1, 1, nil]"], []) + class RubyVM::InstructionSequence + def self.load_iseq(path) + compile_file(path) + end + end + + Coverage.start + tmp = Dir.pwd + require tmp + "/test.rb" + cov = Coverage.peek_result[tmp + "/test.rb"] + coverage_test_snapshot + cov2 = Coverage.peek_result[tmp + "/test.rb"] + p cov + p cov2 + p Coverage.result[tmp + "/test.rb"] + end; + } + } + end + def test_restarting_coverage Dir.mktmpdir {|tmp| Dir.chdir(tmp) {