diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2026-04-29 13:12:51 +0900 |
|---|---|---|
| committer | Takashi Kokubun <takashikkbn@gmail.com> | 2026-05-04 15:06:21 -0700 |
| commit | 4c564ee28e6da275a718e9961aa902bc052c9c1a (patch) | |
| tree | 49dc5569b27141628347aefa3c71ae10fa2e9324 | |
| parent | cb26283b5642e8c43e9a63e8a3eacd5403011bb8 (diff) | |
Fix coverage support for RubyVM::ISeq.compile
[Bug #22018]
ISeq returned by `RubyVM::InstructionSequene.load_iseq` weren't
handled by the coverage module.
| -rw-r--r-- | iseq.c | 17 | ||||
| -rw-r--r-- | test/coverage/test_coverage.rb | 64 |
2 files changed, 75 insertions, 6 deletions
@@ -862,10 +862,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; } @@ -1019,8 +1015,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); } @@ -1188,7 +1189,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; @@ -1376,6 +1377,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); @@ -1440,6 +1442,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) (result.node.parser->newline_list.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); @@ -1825,6 +1828,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; @@ -1899,6 +1903,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self) make_compile_option(&option, opt); int error_state; + iseq_new_setup_coverage(file, (int) (result.node.parser->newline_list.size - 1)); rb_iseq_t *iseq = pm_iseq_new_with_opt(&result.node, rb_fstring_lit("<main>"), file, rb_realpath_internal(Qnil, file, 1), diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 80f8930472..adcd4a946c 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -82,6 +82,70 @@ class TestCoverage < Test::Unit::TestCase } 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) { |
