diff --git a/parse.y b/parse.y index 77b00c7f80..0909356f33 100644 --- a/parse.y +++ b/parse.y @@ -13228,27 +13228,40 @@ local_push(struct parser_params *p, int toplevel_scope) p->lvtbl = local; } +static void +local_free(struct parser_params *p, struct local_vars *local) +{ + if (local->used) { + vtable_free(local->used); + } + +# if WARN_PAST_SCOPE + while (local->past) { + struct vtable *past = local->past; + local->past = past->prev; + vtable_free(past); + } +# endif + + vtable_free(local->args); + vtable_free(local->vars); + + ruby_sized_xfree(local, sizeof(struct local_vars)); +} + static void local_pop(struct parser_params *p) { struct local_vars *local = p->lvtbl->prev; if (p->lvtbl->used) { warn_unused_var(p, p->lvtbl); - vtable_free(p->lvtbl->used); } -# if WARN_PAST_SCOPE - while (p->lvtbl->past) { - struct vtable *past = p->lvtbl->past; - p->lvtbl->past = past->prev; - vtable_free(past); - } -# endif - vtable_free(p->lvtbl->args); - vtable_free(p->lvtbl->vars); + + local_free(p, p->lvtbl); + p->lvtbl = local; + CMDARG_POP(); COND_POP(); - ruby_sized_xfree(p->lvtbl, sizeof(*p->lvtbl)); - p->lvtbl = local; } #ifndef RIPPER @@ -13856,11 +13869,12 @@ rb_ruby_parser_free(void *ptr) if (p->tokenbuf) { ruby_sized_xfree(p->tokenbuf, p->toksiz); } + for (local = p->lvtbl; local; local = prev) { - xfree(local->vars); prev = local->prev; - xfree(local); + local_free(p, local); } + { token_info *ptinfo; while ((ptinfo = p->token_info) != 0) { diff --git a/test/ripper/test_ripper.rb b/test/ripper/test_ripper.rb index 2bb307eddf..25ddc381cc 100644 --- a/test/ripper/test_ripper.rb +++ b/test/ripper/test_ripper.rb @@ -154,6 +154,13 @@ end Ripper.parse("") end end; + + # [Bug #19835] + assert_no_memory_leak(%w(-rripper), "", "#{<<~'end;'}", rss: true) + 1_000_000.times do + Ripper.parse("class Foo") + end + end; end class TestInput < self