Fix leak of token_info when Ripper#warn jumps

For example, the following code leaks:

    class MyRipper < Ripper
      def initialize(src, &blk)
        super(src)
        @blk = blk
      end

      def warn(msg, *args) = @blk.call(msg)
    end

    $VERBOSE = true
    def call_parse = MyRipper.new("if true\n  end\n") { |msg| return msg }.parse

    10.times do
      500_000.times do
        call_parse
      end

      puts `ps -o rss= -p #{$$}`
    end

Before:

    37536
    53744
    70064
    86448
    102576
    119120
    135248
    151216
    167744
    183824

After:

    19280
    19696
    19728
    20336
    20448
    21408
    21616
    21616
    21824
    21840
This commit is contained in:
Peter Zhu 2024-07-31 14:59:21 -04:00
Родитель 60bbd9e462
Коммит 584559d86a
2 изменённых файлов: 23 добавлений и 1 удалений

Просмотреть файл

@ -7203,10 +7203,11 @@ token_info_pop(struct parser_params *p, const char *token, const rb_code_locatio
token_info *ptinfo_beg = p->token_info;
if (!ptinfo_beg) return;
p->token_info = ptinfo_beg->next;
/* indentation check of matched keywords (begin..end, if..end, etc.) */
token_info_warn(p, token, ptinfo_beg, 1, loc);
p->token_info = ptinfo_beg->next;
ruby_sized_xfree(ptinfo_beg, sizeof(*ptinfo_beg));
}

Просмотреть файл

@ -1786,5 +1786,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
call_parse
end
end;
assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
class MyRipper < Ripper
def initialize(src, &blk)
super(src)
@blk = blk
end
def warn(msg, *args) = @blk.call(msg)
end
$VERBOSE = true
def call_parse = MyRipper.new("if true\n end\n") { |msg| return msg }.parse
# Check that call_parse does warn
raise "call_parse should warn" unless call_parse
begin;
1_000_000.times do
call_parse
end
end;
end
end if ripper_test