Skip to content

Commit 36b03a5

Browse files
samuel-williams-shopifyXrXr
authored andcommitted
Preserve line events across jump optimization. (rubyGH-18122) [Backport #22218]
1 parent 41d79e8 commit 36b03a5

2 files changed

Lines changed: 63 additions & 15 deletions

File tree

compile.c

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3652,30 +3652,22 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
36523652
*/
36533653
INSN *nobj = (INSN *)get_destination_insn(iobj);
36543654

3655-
/* This is super nasty hack!!!
3656-
*
3657-
* This jump-jump optimization may ignore event flags of the jump
3658-
* instruction being skipped. Actually, Line 2 TracePoint event
3659-
* is never fired in the following code:
3655+
/* This jump-jump optimization may ignore line events on the jump
3656+
* instruction being skipped. For example, the Line 2 TracePoint
3657+
* event would otherwise never fire in the following code:
36603658
*
36613659
* 1: raise if 1 == 2
36623660
* 2: while true
36633661
* 3: break
36643662
* 4: end
36653663
*
3666-
* This is critical for coverage measurement. [Bug #15980]
3667-
*
3668-
* This is a stopgap measure: stop the jump-jump optimization if
3669-
* coverage measurement is enabled and if the skipped instruction
3670-
* has any event flag.
3671-
*
3672-
* Note that, still, TracePoint Line event does not occur on Line 2.
3673-
* This should be fixed in future.
3664+
* Do not skip a jump that carries a line event. This applies even
3665+
* when coverage is disabled because TracePoint consumes the same
3666+
* event. [Bug #15980]
36743667
*/
36753668
int stop_optimization =
3676-
ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
36773669
nobj->link.type == ISEQ_ELEMENT_INSN &&
3678-
nobj->insn_info.events;
3670+
(nobj->insn_info.events & (RUBY_EVENT_LINE | RUBY_EVENT_COVERAGE_LINE));
36793671
if (!stop_optimization) {
36803672
INSN *pobj = (INSN *)iobj->link.prev;
36813673
int prev_dup = 0;

test/ruby/test_settracefunc.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2830,6 +2830,62 @@ def test_while_in_while
28302830
assert_equal [__LINE__ - 5, __LINE__ - 4, __LINE__ - 3], lines, 'Bug #17868'
28312831
end
28322832

2833+
def test_line_event_after_guard_before_while
2834+
lines = []
2835+
while_line = body_line = nil
2836+
2837+
TracePoint.new(:line) {|tp|
2838+
next unless target_thread?
2839+
lines << tp.lineno
2840+
}.enable {
2841+
raise if 1 == 2
2842+
while_line = __LINE__ + 1
2843+
while true
2844+
body_line = __LINE__ + 1
2845+
break
2846+
end
2847+
}
2848+
2849+
assert_includes lines, while_line
2850+
assert_includes lines, body_line
2851+
assert_operator lines.index(while_line), :<, lines.index(body_line)
2852+
end
2853+
2854+
def test_line_event_after_guard_before_while_predicate
2855+
parent = Class.new do
2856+
def read
2857+
@values.shift
2858+
end
2859+
end
2860+
2861+
child = Class.new(parent) do
2862+
def initialize
2863+
@values = ["chunk", nil]
2864+
end
2865+
end
2866+
2867+
start_line = __LINE__ + 2
2868+
while_line = start_line + 2
2869+
child.class_eval <<~RUBY, __FILE__, start_line
2870+
def read
2871+
return if @finished
2872+
while chunk = super
2873+
chunk.upcase
2874+
end
2875+
end
2876+
RUBY
2877+
2878+
lines = []
2879+
TracePoint.new(:line) {|tp|
2880+
next unless target_thread?
2881+
lines << tp.lineno
2882+
}.enable {
2883+
child.new.read
2884+
}
2885+
2886+
assert_includes lines, while_line
2887+
end
2888+
28332889
def test_allow_reentry
28342890
event_lines = []
28352891
_l1 = _l2 = _l3 = _l4 = nil

0 commit comments

Comments
 (0)