Fix leak in warning of duplicate keys 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: 34832 51952 69760 88048 105344 123040 141152 159152 176656 194272 After: 18400 20256 20272 20272 20272 20304 20368 20368 20368 20400
This commit is contained in:
parent
c0938fd24c
commit
ced35800d4
Notes:
git
2025-04-09 13:49:37 +00:00
15
parse.y
15
parse.y
@ -543,6 +543,8 @@ struct parser_params {
|
|||||||
rb_ast_t *ast;
|
rb_ast_t *ast;
|
||||||
int node_id;
|
int node_id;
|
||||||
|
|
||||||
|
st_table *warn_duplicate_keys_table;
|
||||||
|
|
||||||
int max_numparam;
|
int max_numparam;
|
||||||
ID it_id;
|
ID it_id;
|
||||||
|
|
||||||
@ -14701,7 +14703,7 @@ static void
|
|||||||
warn_duplicate_keys(struct parser_params *p, NODE *hash)
|
warn_duplicate_keys(struct parser_params *p, NODE *hash)
|
||||||
{
|
{
|
||||||
/* See https://bugs.ruby-lang.org/issues/20331 for discussion about what is warned. */
|
/* See https://bugs.ruby-lang.org/issues/20331 for discussion about what is warned. */
|
||||||
st_table *literal_keys = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
|
p->warn_duplicate_keys_table = st_init_table_with_size(&literal_type, RNODE_LIST(hash)->as.nd_alen / 2);
|
||||||
while (hash && RNODE_LIST(hash)->nd_next) {
|
while (hash && RNODE_LIST(hash)->nd_next) {
|
||||||
NODE *head = RNODE_LIST(hash)->nd_head;
|
NODE *head = RNODE_LIST(hash)->nd_head;
|
||||||
NODE *value = RNODE_LIST(hash)->nd_next;
|
NODE *value = RNODE_LIST(hash)->nd_next;
|
||||||
@ -14717,16 +14719,17 @@ warn_duplicate_keys(struct parser_params *p, NODE *hash)
|
|||||||
if (nd_type_st_key_enable_p(head)) {
|
if (nd_type_st_key_enable_p(head)) {
|
||||||
key = (st_data_t)head;
|
key = (st_data_t)head;
|
||||||
|
|
||||||
if (st_delete(literal_keys, &key, &data)) {
|
if (st_delete(p->warn_duplicate_keys_table, &key, &data)) {
|
||||||
rb_warn2L(nd_line((NODE *)data),
|
rb_warn2L(nd_line((NODE *)data),
|
||||||
"key %+"PRIsWARN" is duplicated and overwritten on line %d",
|
"key %+"PRIsWARN" is duplicated and overwritten on line %d",
|
||||||
nd_value(p, head), WARN_I(nd_line(head)));
|
nd_value(p, head), WARN_I(nd_line(head)));
|
||||||
}
|
}
|
||||||
st_insert(literal_keys, (st_data_t)key, (st_data_t)hash);
|
st_insert(p->warn_duplicate_keys_table, (st_data_t)key, (st_data_t)hash);
|
||||||
}
|
}
|
||||||
hash = next;
|
hash = next;
|
||||||
}
|
}
|
||||||
st_free_table(literal_keys);
|
st_free_table(p->warn_duplicate_keys_table);
|
||||||
|
p->warn_duplicate_keys_table = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NODE *
|
static NODE *
|
||||||
@ -15612,6 +15615,10 @@ rb_ruby_parser_free(void *ptr)
|
|||||||
rb_ast_free(p->ast);
|
rb_ast_free(p->ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p->warn_duplicate_keys_table) {
|
||||||
|
st_free_table(p->warn_duplicate_keys_table);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef RIPPER
|
#ifndef RIPPER
|
||||||
if (p->tokens) {
|
if (p->tokens) {
|
||||||
rb_parser_ary_free(p, p->tokens);
|
rb_parser_ary_free(p, p->tokens);
|
||||||
|
@ -1765,4 +1765,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_return_out_of_warn_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 warn(msg, *args) = @blk.call(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_parse = MyRipper.new("{ a: 1, a: 2 }") { |msg| return msg }.parse
|
||||||
|
|
||||||
|
# Check that call_parse does warn
|
||||||
|
raise "call_parse should warn" unless call_parse
|
||||||
|
begin;
|
||||||
|
500_000.times do
|
||||||
|
call_parse
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
end
|
||||||
end if ripper_test
|
end if ripper_test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user