From a4a19b114ba94b8f28d5a91aee5d595a516006d5 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 1 Aug 2019 14:41:21 -0400 Subject: [PATCH] Allow non-finalizable objects in ObjectSpace::WeakMap [feature #16035] This goes one step farther than what nobu did in [feature #13498] With this patch, special objects such as static symbols, integers, etc can be used as either key or values inside WeakMap. They simply don't have a finalizer defined on them. This is useful if you need to deduplicate value objects --- gc.c | 20 +++++++++----------- test/ruby/test_weakmap.rb | 31 +++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/gc.c b/gc.c index 18ae189421..c083fbb297 100644 --- a/gc.c +++ b/gc.c @@ -2986,18 +2986,12 @@ should_be_callable(VALUE block) } static void -should_be_finalizable_internal(VALUE obj) +should_be_finalizable(VALUE obj) { if (!FL_ABLE(obj)) { rb_raise(rb_eArgError, "cannot define finalizer for %s", rb_obj_classname(obj)); } -} - -static void -should_be_finalizable(VALUE obj) -{ - should_be_finalizable_internal(obj); rb_check_frozen(obj); } @@ -10253,6 +10247,7 @@ wmap_allocate(VALUE klass) static int wmap_live_p(rb_objspace_t *objspace, VALUE obj) { + if (!FL_ABLE(obj)) return TRUE; if (!is_id_value(objspace, obj)) return FALSE; if (!is_live_object(objspace, obj)) return FALSE; return TRUE; @@ -10510,10 +10505,13 @@ wmap_aset(VALUE self, VALUE wmap, VALUE orig) struct weakmap *w; TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w); - should_be_finalizable_internal(orig); - should_be_finalizable_internal(wmap); - define_final0(orig, w->final); - define_final0(wmap, w->final); + if (FL_ABLE(orig)) { + define_final0(orig, w->final); + } + if (FL_ABLE(wmap)) { + define_final0(wmap, w->final); + } + st_update(w->obj2wmap, (st_data_t)orig, wmap_aset_update, wmap); st_insert(w->wmap2obj, (st_data_t)wmap, (st_data_t)orig); return nonspecial_obj_id(orig); diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb index 8eb32b574f..3b9eef770a 100644 --- a/test/ruby/test_weakmap.rb +++ b/test/ruby/test_weakmap.rb @@ -16,16 +16,27 @@ class TestWeakMap < Test::Unit::TestCase def test_aset_const x = Object.new - assert_raise(ArgumentError) {@wm[true] = x} - assert_raise(ArgumentError) {@wm[false] = x} - assert_raise(ArgumentError) {@wm[nil] = x} - assert_raise(ArgumentError) {@wm[42] = x} - assert_raise(ArgumentError) {@wm[:foo] = x} - assert_raise(ArgumentError) {@wm[x] = true} - assert_raise(ArgumentError) {@wm[x] = false} - assert_raise(ArgumentError) {@wm[x] = nil} - assert_raise(ArgumentError) {@wm[x] = 42} - assert_raise(ArgumentError) {@wm[x] = :foo} + @wm[true] = x + assert_same(x, @wm[true]) + @wm[false] = x + assert_same(x, @wm[false]) + @wm[nil] = x + assert_same(x, @wm[nil]) + @wm[42] = x + assert_same(x, @wm[42]) + @wm[:foo] = x + assert_same(x, @wm[:foo]) + + @wm[x] = true + assert_same(true, @wm[x]) + @wm[x] = false + assert_same(false, @wm[x]) + @wm[x] = nil + assert_same(nil, @wm[x]) + @wm[x] = 42 + assert_same(42, @wm[x]) + @wm[x] = :foo + assert_same(:foo, @wm[x]) end def assert_weak_include(m, k, n = 100)