Fix false LocalJumpError when branch coverage is enabled
`throw TAG_BREAK` instruction makes a jump only if the continuation of catch of TAG_BREAK exactly matches the instruction immediately following the "send" instruction that is currently being executed. Otherwise, it seems to determine break from proc-closure. Branch coverage may insert some recording instructions after "send" instruction, which broke the conditions for TAG_BREAK to work properly. This change forces to set the continuation of catch of TAG_BREAK immediately after "send" (or "invokesuper") instruction. [Bug #18991]
This commit is contained in:
parent
f7db1affd1
commit
4a7d6c2852
Notes:
git
2022-11-08 05:37:26 +00:00
25
compile.c
25
compile.c
|
@ -7449,7 +7449,30 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
||||||
ISEQ_TYPE_BLOCK, line);
|
ISEQ_TYPE_BLOCK, line);
|
||||||
CHECK(COMPILE(ret, "iter caller", node->nd_iter));
|
CHECK(COMPILE(ret, "iter caller", node->nd_iter));
|
||||||
}
|
}
|
||||||
ADD_LABEL(ret, retry_end_l);
|
|
||||||
|
{
|
||||||
|
// We need to put the label "retry_end_l" immediately after the last "send" instruction.
|
||||||
|
// This because vm_throw checks if the break cont is equal to the index of next insn of the "send".
|
||||||
|
// (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".)
|
||||||
|
//
|
||||||
|
// Normally, "send" instruction is at the last.
|
||||||
|
// However, qcall under branch coverage measurement adds some instructions after the "send".
|
||||||
|
//
|
||||||
|
// Note that "invokesuper" appears instead of "send".
|
||||||
|
INSN *iobj;
|
||||||
|
LINK_ELEMENT *last_elem = LAST_ELEMENT(ret);
|
||||||
|
iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem);
|
||||||
|
while (INSN_OF(iobj) != BIN(send) && INSN_OF(iobj) != BIN(invokesuper)) {
|
||||||
|
iobj = (INSN*) get_prev_insn(iobj);
|
||||||
|
}
|
||||||
|
ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l);
|
||||||
|
|
||||||
|
// LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it
|
||||||
|
// even if we add an insn to the last of LINK_ANCHOR. So this updates it manually.
|
||||||
|
if (&iobj->link == LAST_ELEMENT(ret)) {
|
||||||
|
ret->last = (LINK_ELEMENT*) retry_end_l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (popped) {
|
if (popped) {
|
||||||
ADD_INSN(ret, line_node, pop);
|
ADD_INSN(ret, line_node, pop);
|
||||||
|
|
|
@ -964,4 +964,18 @@ class TestCoverage < Test::Unit::TestCase
|
||||||
p :NG
|
p :NG
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_tag_break_with_branch_coverage
|
||||||
|
result = {
|
||||||
|
:branches => {
|
||||||
|
[:"&.", 0, 1, 0, 1, 6] => {
|
||||||
|
[:then, 1, 1, 0, 1, 6] => 1,
|
||||||
|
[:else, 2, 1, 0, 1, 6] => 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert_coverage(<<~"end;", { branches: true }, result)
|
||||||
|
1&.tap do break end
|
||||||
|
end;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue