summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-09-17 20:19:57 +1200
committerSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-09-22 22:19:12 +1200
commit9434a7333c2a23c680a977331a60ca7c502c1ac0 (patch)
treea0715dd55cdab3816b7065a56815017d2d0139c6
parent4c37eaa979d89be14c2142659daba0588aa64912 (diff)
Enable coverage for eval.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/6396
-rw-r--r--compile.c6
-rw-r--r--iseq.c13
-rw-r--r--spec/ruby/library/coverage/result_spec.rb34
-rw-r--r--test/coverage/test_coverage.rb17
-rw-r--r--vm_eval.c8
5 files changed, 64 insertions, 14 deletions
diff --git a/compile.c b/compile.c
index 45cb116983..7d553bfba1 100644
--- a/compile.c
+++ b/compile.c
@@ -2308,9 +2308,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
if (ISEQ_COVERAGE(iseq)) {
if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) &&
!(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) {
- int line = iobj->insn_info.line_no;
- if (line >= 1) {
- RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0));
+ int line = iobj->insn_info.line_no - 1;
+ if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) {
+ RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0));
}
}
if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) {
diff --git a/iseq.c b/iseq.c
index 4892d93df1..9359fcfe4e 100644
--- a/iseq.c
+++ b/iseq.c
@@ -697,7 +697,6 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
-
if (option->coverage_enabled) {
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
@@ -928,6 +927,18 @@ rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_
rb_iseq_t *
rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth)
{
+ VALUE coverages = rb_get_coverages();
+ if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
+ int line_offset = RB_NUM2INT(first_lineno) - 1;
+ int line_count = line_offset + ast_line_count(ast);
+
+ if (line_count >= 0) {
+ int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
+ VALUE coverage = rb_default_coverage(len);
+ rb_hash_aset(coverages, path, coverage);
+ }
+ }
+
return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno,
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT);
}
diff --git a/spec/ruby/library/coverage/result_spec.rb b/spec/ruby/library/coverage/result_spec.rb
index 4cc43e8462..61283e4545 100644
--- a/spec/ruby/library/coverage/result_spec.rb
+++ b/spec/ruby/library/coverage/result_spec.rb
@@ -91,15 +91,31 @@ describe 'Coverage.result' do
Coverage.result.should_not include(@config_file)
end
- it 'returns the correct results when eval is used' do
- Coverage.start
- require @eval_code_file.chomp('.rb')
- result = Coverage.result
+ ruby_version_is '3.1'...'3.2' do
+ it 'returns the correct results when eval is used' do
+ Coverage.start
+ require @eval_code_file.chomp('.rb')
+ result = Coverage.result
- result.should == {
- @eval_code_file => [
- 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
- ]
- }
+ result.should == {
+ @eval_code_file => [
+ 1, nil, 1, nil, 1, nil, nil, nil, nil, nil, 1
+ ]
+ }
+ end
+ end
+
+ ruby_version_is '3.2' do
+ it 'returns the correct results when eval is used' do
+ Coverage.start
+ require @eval_code_file.chomp('.rb')
+ result = Coverage.result
+
+ result.should == {
+ @eval_code_file => [
+ 1, nil, 1, nil, 1, 1, nil, nil, nil, nil, 1
+ ]
+ }
+ end
end
end
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index eefe7e7da6..1bb85b67b3 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -136,7 +136,7 @@ class TestCoverage < Test::Unit::TestCase
f.puts 'REPEATS = 400'
f.puts 'def add_method(target)'
f.puts ' REPEATS.times do'
- f.puts ' target.class_eval(<<~RUBY, __FILE__, __LINE__ + 1)'
+ f.puts ' target.class_eval(<<~RUBY)'
f.puts ' def foo'
f.puts ' #{"\n" * rand(REPEATS)}'
f.puts ' end'
@@ -157,6 +157,21 @@ class TestCoverage < Test::Unit::TestCase
}
end
+ def test_eval_coverage
+ assert_in_out_err(%w[-rcoverage], <<-"end;", ["[1, nil, 1, nil]"], [])
+ Coverage.start
+
+ eval(<<-RUBY, TOPLEVEL_BINDING, "test.rb")
+ s = String.new
+ begin
+ s << "foo
+ bar".freeze; end
+ RUBY
+
+ p Coverage.result["test.rb"]
+ end;
+ end
+
def test_nocoverage_optimized_line
assert_ruby_status(%w[], "#{<<-"begin;"}\n#{<<-'end;'}")
begin;
diff --git a/vm_eval.c b/vm_eval.c
index c7ad71e279..9d7a736ad9 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1672,6 +1672,8 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
rb_iseq_t *iseq = NULL;
rb_ast_t *ast;
int isolated_depth = 0;
+ int coverage_enabled = Qtrue;
+
{
int depth = 1;
const VALUE *ep = vm_block_ep(base_block);
@@ -1703,11 +1705,17 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind,
rb_gc_register_mark_object(eval_default_path);
}
fname = eval_default_path;
+ coverage_enabled = Qfalse;
}
rb_parser_set_context(parser, parent, FALSE);
ast = rb_parser_compile_string_path(parser, fname, src, line);
if (ast->body.root) {
+ if (ast->body.compile_option == Qnil) {
+ ast->body.compile_option = rb_obj_hide(rb_ident_hash_new());
+ }
+ rb_hash_aset(ast->body.compile_option, rb_sym_intern_ascii_cstr("coverage_enabled"), coverage_enabled);
+
iseq = rb_iseq_new_eval(&ast->body,
ISEQ_BODY(parent)->location.label,
fname, Qnil, INT2FIX(line),