зеркало из https://github.com/github/ruby.git
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:
Родитель
c0938fd24c
Коммит
ced35800d4
15
parse.y
15
parse.y
|
@ -543,6 +543,8 @@ struct parser_params {
|
|||
rb_ast_t *ast;
|
||||
int node_id;
|
||||
|
||||
st_table *warn_duplicate_keys_table;
|
||||
|
||||
int max_numparam;
|
||||
ID it_id;
|
||||
|
||||
|
@ -14701,7 +14703,7 @@ static void
|
|||
warn_duplicate_keys(struct parser_params *p, NODE *hash)
|
||||
{
|
||||
/* 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) {
|
||||
NODE *head = RNODE_LIST(hash)->nd_head;
|
||||
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)) {
|
||||
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),
|
||||
"key %+"PRIsWARN" is duplicated and overwritten on line %d",
|
||||
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;
|
||||
}
|
||||
st_free_table(literal_keys);
|
||||
st_free_table(p->warn_duplicate_keys_table);
|
||||
p->warn_duplicate_keys_table = NULL;
|
||||
}
|
||||
|
||||
static NODE *
|
||||
|
@ -15612,6 +15615,10 @@ rb_ruby_parser_free(void *ptr)
|
|||
rb_ast_free(p->ast);
|
||||
}
|
||||
|
||||
if (p->warn_duplicate_keys_table) {
|
||||
st_free_table(p->warn_duplicate_keys_table);
|
||||
}
|
||||
|
||||
#ifndef RIPPER
|
||||
if (p->tokens) {
|
||||
rb_parser_ary_free(p, p->tokens);
|
||||
|
|
|
@ -1765,4 +1765,26 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
|
|||
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
|
||||
|
|
Загрузка…
Ссылка в новой задаче