From bfbe1b9b31aaf678c4cfe2be09e78acb42629c7f Mon Sep 17 00:00:00 2001 From: glass Date: Sun, 1 Dec 2013 05:28:54 +0000 Subject: * hash.c (rb_hash_rehash): fix to free new st_table when exception is raised in do_hash(). [Bug #9187] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43942 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- hash.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) (limited to 'hash.c') diff --git a/hash.c b/hash.c index e347c79192..d312325981 100644 --- a/hash.c +++ b/hash.c @@ -576,6 +576,11 @@ rb_hash_s_try_convert(VALUE dummy, VALUE hash) return rb_check_hash_type(hash); } +struct rehash_arg { + VALUE hash; + st_table *tbl; +}; + static int rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) { @@ -585,6 +590,14 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) return ST_CONTINUE; } +static VALUE +rehash_func(VALUE arg) +{ + struct rehash_arg *p = (struct rehash_arg *)arg; + rb_hash_foreach(p->hash, rb_hash_rehash_i, (VALUE)p->tbl); + return Qnil; +} + /* * call-seq: * hsh.rehash -> hsh @@ -608,18 +621,30 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) static VALUE rb_hash_rehash(VALUE hash) { - st_table *tbl; + int state; + struct rehash_arg arg; + st_table *new_tbl, *old_tbl = RHASH(hash)->ntbl; if (RHASH_ITER_LEV(hash) > 0) { rb_raise(rb_eRuntimeError, "rehash during iteration"); } rb_hash_modify_check(hash); - if (!RHASH(hash)->ntbl) - return hash; - tbl = st_init_table_with_size(RHASH(hash)->ntbl->type, RHASH(hash)->ntbl->num_entries); - rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl); - st_free_table(RHASH(hash)->ntbl); - RHASH(hash)->ntbl = tbl; + if (!old_tbl) return hash; + + new_tbl = st_init_table_with_size(old_tbl->type, old_tbl->num_entries); + arg.hash = hash; + arg.tbl = new_tbl; + + rb_protect(rehash_func, (VALUE)&arg, &state); + + if (state) { + st_free_table(new_tbl); + rb_jump_tag(state); + } + else { + st_free_table(RHASH(hash)->ntbl); + RHASH(hash)->ntbl = new_tbl; + } return hash; } -- cgit v1.2.3