summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2024-07-31 13:32:29 -0400
committerPeter Zhu <peter@peterzhu.ca>2024-07-31 14:47:44 -0400
commit635839749026b5d5bafeb892f2f01a9348a4b5c8 (patch)
treeae9b62749e81ebb598c4bdfcce8e7ee4ab705bee
parent70b4f45d9f1936128e73ab0b8701269c06e709f5 (diff)
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
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/11287
-rw-r--r--parse.y4
-rw-r--r--test/ripper/test_parser_events.rb22
2 files changed, 26 insertions, 0 deletions
diff --git a/parse.y b/parse.y
index a68fb63402..e73628b8cc 100644
--- a/parse.y
+++ b/parse.y
@@ -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);
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 5434acb523..35264868b9 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -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