Fix leak of AST when Ripper#compile_error jumps

For example, the following script leaks:

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

      def compile_error(msg) = @blk.call(msg)
    end

    def call_parse = MyRipper.new("/") { |msg| return msg }.parse

    10.times do
      100_000.times do
        call_parse
      end

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

Before:

    93952
    169040
    244224
    318784
    394432
    468224
    544048
    618560
    693776
    768384

After:

    19776
    19776
    20352
    20880
    20912
    21408
    21328
    21152
    21472
    20944
This commit is contained in:
Peter Zhu 2024-07-31 13:32:29 -04:00
Родитель 70b4f45d9f
Коммит 6358397490
2 изменённых файлов: 26 добавлений и 0 удалений

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

@ -15608,6 +15608,10 @@ rb_ruby_parser_free(void *ptr)
struct parser_params *p = (struct parser_params*)ptr;
struct local_vars *local, *prev;
if (p->ast) {
rb_ast_free(p->ast);
}
#ifndef RIPPER
if (p->tokens) {
rb_parser_ary_free(p, p->tokens);

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

@ -1743,4 +1743,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
parse('case 0; in {a:}; end', :on_hshptn) {thru_hshptn = true}
assert_equal true, thru_hshptn
end
def test_return_out_of_compile_error_no_memory_leak
assert_no_memory_leak(%w(-rripper), "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
class MyRipper < Ripper
def initialize(src, &blk)
super(src)
@blk = blk
end
def compile_error(msg) = @blk.call(msg)
end
def call_parse = MyRipper.new("/") { |msg| return msg }.parse
# Check that call_parse does return a syntax error
raise "call_parse should return a syntax error" unless call_parse
begin;
100_000.times do
call_parse
end
end;
end
end if ripper_test