diff options
author | glass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-01 05:28:54 +0000 |
---|---|---|
committer | glass <glass@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2013-12-01 05:28:54 +0000 |
commit | bfbe1b9b31aaf678c4cfe2be09e78acb42629c7f (patch) | |
tree | b511b1d1dfba5dc7a83fd1b44e04bb9effced0a2 /hash.c | |
parent | 64ac49e6fea659ff59d5db5f2dfdac4cef8e199c (diff) | |
download | ruby-bfbe1b9b31aaf678c4cfe2be09e78acb42629c7f.tar.gz |
* 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
Diffstat (limited to 'hash.c')
-rw-r--r-- | hash.c | 39 |
1 files changed, 32 insertions, 7 deletions
@@ -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; } |