aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-03-06 14:45:02 -0500
committerPeter Zhu <peter@peterzhu.ca>2023-03-10 09:32:10 -0500
commitf98a7fd28d6eedfb80e5d7d1a92d67aa3dc4783f (patch)
tree708f7767536d811c812393ffb1d2a71039bef09b
parentdd47ce10dbfca9292e88bbbd3d7f37e3ea503034 (diff)
downloadruby-f98a7fd28d6eedfb80e5d7d1a92d67aa3dc4783f.tar.gz
Move WeakMap and WeakKeyMap code to weakmap.c
These classes don't belong in gc.c as they're not actually part of the GC. This commit refactors the code by moving all the code into a weakmap.c file.
-rw-r--r--common.mk165
-rw-r--r--gc.c945
-rw-r--r--inits.c1
-rw-r--r--internal/gc.h3
-rw-r--r--weakmap.c800
5 files changed, 1031 insertions, 883 deletions
diff --git a/common.mk b/common.mk
index 1195f81b8a..e224bf28a7 100644
--- a/common.mk
+++ b/common.mk
@@ -150,6 +150,7 @@ COMMONOBJS = array.$(OBJEXT) \
vm_dump.$(OBJEXT) \
vm_sync.$(OBJEXT) \
vm_trace.$(OBJEXT) \
+ weakmap.$(OBJEXT) \
$(YJIT_OBJ) \
$(YJIT_LIBOBJ) \
$(COROUTINE_OBJ) \
@@ -17999,6 +18000,170 @@ vm_trace.$(OBJEXT): {$(VPATH)}vm_core.h
vm_trace.$(OBJEXT): {$(VPATH)}vm_opts.h
vm_trace.$(OBJEXT): {$(VPATH)}vm_trace.c
vm_trace.$(OBJEXT): {$(VPATH)}yjit.h
+weakmap.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/compilers.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/gc.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/hash.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/proc.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
+weakmap.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+weakmap.$(OBJEXT): {$(VPATH)}assert.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/assume.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/bool.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/limits.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
+weakmap.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
+weakmap.$(OBJEXT): {$(VPATH)}config.h
+weakmap.$(OBJEXT): {$(VPATH)}defines.h
+weakmap.$(OBJEXT): {$(VPATH)}intern.h
+weakmap.$(OBJEXT): {$(VPATH)}internal.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/abi.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/anyargs.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/assume.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/const.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/error.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/format.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/cast.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/config.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/constant_p.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/robject.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/ctype.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/dllexport.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/dosish.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/error.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/eval.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/event.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/fl_type.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/gc.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/glob.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/globals.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/extension.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/feature.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/has/warning.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/array.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/class.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/error.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/file.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/io.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/load.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/object.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/process.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/random.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/range.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/re.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/select.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/string.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/time.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/interpreter.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/iterator.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/memory.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/method.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/module.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/newobj.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/scan_args.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/special_consts.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/static_assert.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/stdalign.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/stdbool.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/symbol.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/value.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/value_type.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/variable.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/warning_push.h
+weakmap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
+weakmap.$(OBJEXT): {$(VPATH)}missing.h
+weakmap.$(OBJEXT): {$(VPATH)}st.h
+weakmap.$(OBJEXT): {$(VPATH)}subst.h
+weakmap.$(OBJEXT): {$(VPATH)}weakmap.c
yjit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
yjit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
yjit.$(OBJEXT): $(CCAN_DIR)/list/list.h
diff --git a/gc.c b/gc.c
index bbcdc4cedc..fdb6334a9d 100644
--- a/gc.c
+++ b/gc.c
@@ -1183,10 +1183,8 @@ total_freed_pages(rb_objspace_t *objspace)
#define is_lazy_sweeping(objspace) (GC_ENABLE_LAZY_SWEEP && has_sweeping_pages(objspace))
#if SIZEOF_LONG == SIZEOF_VOIDP
-# define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
# define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
-# define nonspecial_obj_id(obj) LL2NUM((SIGNED_VALUE)(obj) / 2)
# define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \
((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1))
#else
@@ -1220,8 +1218,6 @@ void rb_vm_update_references(void *ptr);
void rb_gcdebug_print_obj_condition(VALUE obj);
-static VALUE define_final0(VALUE obj, VALUE block);
-
NORETURN(static void *gc_vraise(void *ptr));
NORETURN(static void gc_raise(VALUE exc, const char *fmt, ...));
NORETURN(static void negative_size_allocation_error(const char *));
@@ -4235,6 +4231,45 @@ should_be_finalizable(VALUE obj)
rb_check_frozen(obj);
}
+VALUE
+rb_define_finalizer_no_check(VALUE obj, VALUE block)
+{
+ rb_objspace_t *objspace = &rb_objspace;
+ VALUE table;
+ st_data_t data;
+
+ RBASIC(obj)->flags |= FL_FINALIZE;
+
+ if (st_lookup(finalizer_table, obj, &data)) {
+ table = (VALUE)data;
+
+ /* avoid duplicate block, table is usually small */
+ {
+ long len = RARRAY_LEN(table);
+ long i;
+
+ for (i = 0; i < len; i++) {
+ VALUE recv = RARRAY_AREF(table, i);
+ if (rb_equal(recv, block)) {
+ block = recv;
+ goto end;
+ }
+ }
+ }
+
+ rb_ary_push(table, block);
+ }
+ else {
+ table = rb_ary_new3(1, block);
+ RBASIC_CLEAR_CLASS(table);
+ st_add_direct(finalizer_table, obj, table);
+ }
+ end:
+ block = rb_ary_new3(2, INT2FIX(0), block);
+ OBJ_FREEZE(block);
+ return block;
+}
+
/*
* call-seq:
* ObjectSpace.define_finalizer(obj, aProc=proc())
@@ -4315,46 +4350,7 @@ define_final(int argc, VALUE *argv, VALUE os)
rb_warn("finalizer references object to be finalized");
}
- return define_final0(obj, block);
-}
-
-static VALUE
-define_final0(VALUE obj, VALUE block)
-{
- rb_objspace_t *objspace = &rb_objspace;
- VALUE table;
- st_data_t data;
-
- RBASIC(obj)->flags |= FL_FINALIZE;
-
- if (st_lookup(finalizer_table, obj, &data)) {
- table = (VALUE)data;
-
- /* avoid duplicate block, table is usually small */
- {
- long len = RARRAY_LEN(table);
- long i;
-
- for (i = 0; i < len; i++) {
- VALUE recv = RARRAY_AREF(table, i);
- if (rb_equal(recv, block)) {
- block = recv;
- goto end;
- }
- }
- }
-
- rb_ary_push(table, block);
- }
- else {
- table = rb_ary_new3(1, block);
- RBASIC_CLEAR_CLASS(table);
- st_add_direct(finalizer_table, obj, table);
- }
- end:
- block = rb_ary_new3(2, INT2FIX(0), block);
- OBJ_FREEZE(block);
- return block;
+ return rb_define_finalizer_no_check(obj, block);
}
VALUE
@@ -4362,7 +4358,7 @@ rb_define_finalizer(VALUE obj, VALUE block)
{
should_be_finalizable(obj);
should_be_callable(block);
- return define_final0(obj, block);
+ return rb_define_finalizer_no_check(obj, block);
}
void
@@ -4688,9 +4684,18 @@ rb_objspace_garbage_object_p(VALUE obj)
return is_garbage_object(objspace, obj);
}
-static VALUE
-id2ref_obj_tbl(rb_objspace_t *objspace, VALUE objid)
+bool
+rb_gc_is_ptr_to_obj(void *ptr)
+{
+ rb_objspace_t *objspace = &rb_objspace;
+ return is_pointer_to_heap(objspace, ptr);
+}
+
+VALUE
+rb_gc_id2ref_obj_tbl(VALUE objid)
{
+ rb_objspace_t *objspace = &rb_objspace;
+
VALUE orig;
if (st_lookup(objspace->id_to_obj_tbl, objid, &orig)) {
return orig;
@@ -4747,7 +4752,7 @@ id2ref(VALUE objid)
}
}
- if (!UNDEF_P(orig = id2ref_obj_tbl(objspace, objid)) &&
+ if (!UNDEF_P(orig = rb_gc_id2ref_obj_tbl(objid)) &&
is_live_object(objspace, orig)) {
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(orig)) {
@@ -4821,16 +4826,21 @@ cached_object_id(VALUE obj)
}
static VALUE
-nonspecial_obj_id_(VALUE obj)
+nonspecial_obj_id(VALUE obj)
{
- return nonspecial_obj_id(obj);
+#if SIZEOF_LONG == SIZEOF_VOIDP
+ return (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG);
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+ return LL2NUM((SIGNED_VALUE)(obj) / 2);
+#else
+# error not supported
+#endif
}
-
VALUE
rb_memory_id(VALUE obj)
{
- return rb_find_object_id(obj, nonspecial_obj_id_);
+ return rb_find_object_id(obj, nonspecial_obj_id);
}
/*
@@ -12869,787 +12879,6 @@ rb_gc_adjust_memory_usage(ssize_t diff)
}
/*
- ------------------------------ WeakMap ------------------------------
-*/
-
-struct weakmap {
- st_table *obj2wmap; /* obj -> [ref,...] */
- st_table *wmap2obj; /* ref -> obj */
- VALUE final;
-};
-
-#define WMAP_DELETE_DEAD_OBJECT_IN_MARK 0
-
-#if WMAP_DELETE_DEAD_OBJECT_IN_MARK
-static int
-wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
- VALUE obj = (VALUE)val;
- if (!is_live_object(objspace, obj)) return ST_DELETE;
- return ST_CONTINUE;
-}
-#endif
-
-static void
-wmap_compact(void *ptr)
-{
- struct weakmap *w = ptr;
- if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj);
- if (w->obj2wmap) rb_gc_update_tbl_refs(w->obj2wmap);
- w->final = rb_gc_location(w->final);
-}
-
-static void
-wmap_mark(void *ptr)
-{
- struct weakmap *w = ptr;
-#if WMAP_DELETE_DEAD_OBJECT_IN_MARK
- if (w->obj2wmap) st_foreach(w->obj2wmap, wmap_mark_map, (st_data_t)&rb_objspace);
-#endif
- rb_gc_mark_movable(w->final);
-}
-
-static int
-wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
-{
- VALUE *ptr = (VALUE *)val;
- ruby_sized_xfree(ptr, (ptr[0] + 1) * sizeof(VALUE));
- return ST_CONTINUE;
-}
-
-static void
-wmap_free(void *ptr)
-{
- struct weakmap *w = ptr;
- st_foreach(w->obj2wmap, wmap_free_map, 0);
- st_free_table(w->obj2wmap);
- st_free_table(w->wmap2obj);
- xfree(w);
-}
-
-static int
-wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
-{
- VALUE *ptr = (VALUE *)val;
- *(size_t *)arg += (ptr[0] + 1) * sizeof(VALUE);
- return ST_CONTINUE;
-}
-
-static size_t
-wmap_memsize(const void *ptr)
-{
- size_t size;
- const struct weakmap *w = ptr;
- size = sizeof(*w);
- size += st_memsize(w->obj2wmap);
- size += st_memsize(w->wmap2obj);
- st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
- return size;
-}
-
-static const rb_data_type_t weakmap_type = {
- "weakmap",
- {
- wmap_mark,
- wmap_free,
- wmap_memsize,
- wmap_compact,
- },
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
-};
-
-static VALUE wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self));
-
-static VALUE
-wmap_allocate(VALUE klass)
-{
- struct weakmap *w;
- VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
- w->obj2wmap = rb_init_identtable();
- w->wmap2obj = rb_init_identtable();
- w->final = rb_func_lambda_new(wmap_finalize, obj, 1, 1);
- return obj;
-}
-
-static int
-wmap_live_p(rb_objspace_t *objspace, VALUE obj)
-{
- if (SPECIAL_CONST_P(obj)) return TRUE;
- /* If is_pointer_to_heap returns false, the page could be in the tomb heap
- * or have already been freed. */
- if (!is_pointer_to_heap(objspace, (void *)obj)) return FALSE;
-
- void *poisoned = asan_unpoison_object_temporary(obj);
-
- enum ruby_value_type t = BUILTIN_TYPE(obj);
- int ret = (!(t == T_NONE || t >= T_FIXNUM || t == T_ICLASS) &&
- is_live_object(objspace, obj));
-
- if (poisoned) {
- asan_poison_object(obj);
- }
-
- return ret;
-}
-
-static int
-wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
-{
- VALUE wmap, *ptr, size, i, j;
- if (!existing) return ST_STOP;
- wmap = (VALUE)arg, ptr = (VALUE *)*value;
- for (i = j = 1, size = ptr[0]; i <= size; ++i) {
- if (ptr[i] != wmap) {
- ptr[j++] = ptr[i];
- }
- }
- if (j == 1) {
- ruby_sized_xfree(ptr, i * sizeof(VALUE));
- return ST_DELETE;
- }
- if (j < i) {
- SIZED_REALLOC_N(ptr, VALUE, j + 1, i);
- ptr[0] = j;
- *value = (st_data_t)ptr;
- }
- return ST_CONTINUE;
-}
-
-/* :nodoc: */
-static VALUE
-wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
-{
- st_data_t orig, wmap, data;
- VALUE obj, *rids, i, size;
- struct weakmap *w;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- /* Get reference from object id. */
- if (UNDEF_P(obj = id2ref_obj_tbl(&rb_objspace, objid))) {
- rb_bug("wmap_finalize: objid is not found.");
- }
-
- /* obj is original referenced object and/or weak reference. */
- orig = (st_data_t)obj;
- if (st_delete(w->obj2wmap, &orig, &data)) {
- rids = (VALUE *)data;
- size = *rids++;
- for (i = 0; i < size; ++i) {
- wmap = (st_data_t)rids[i];
- st_delete(w->wmap2obj, &wmap, NULL);
- }
- ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
- }
-
- wmap = (st_data_t)obj;
- if (st_delete(w->wmap2obj, &wmap, &orig)) {
- wmap = (st_data_t)obj;
- st_update(w->obj2wmap, orig, wmap_final_func, wmap);
- }
- return self;
-}
-
-struct wmap_iter_arg {
- rb_objspace_t *objspace;
- VALUE value;
-};
-
-static VALUE
-wmap_inspect_append(rb_objspace_t *objspace, VALUE str, VALUE obj)
-{
- if (SPECIAL_CONST_P(obj)) {
- return rb_str_append(str, rb_inspect(obj));
- }
- else if (wmap_live_p(objspace, obj)) {
- return rb_str_append(str, rb_any_to_s(obj));
- }
- else {
- return rb_str_catf(str, "#<collected:%p>", (void*)obj);
- }
-}
-
-static int
-wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
- rb_objspace_t *objspace = argp->objspace;
- VALUE str = argp->value;
- VALUE k = (VALUE)key, v = (VALUE)val;
-
- if (RSTRING_PTR(str)[0] == '#') {
- rb_str_cat2(str, ", ");
- }
- else {
- rb_str_cat2(str, ": ");
- RSTRING_PTR(str)[0] = '#';
- }
- wmap_inspect_append(objspace, str, k);
- rb_str_cat2(str, " => ");
- wmap_inspect_append(objspace, str, v);
-
- return ST_CONTINUE;
-}
-
-static VALUE
-wmap_inspect(VALUE self)
-{
- VALUE str;
- VALUE c = rb_class_name(CLASS_OF(self));
- struct weakmap *w;
- struct wmap_iter_arg args;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
- if (w->wmap2obj) {
- args.objspace = &rb_objspace;
- args.value = str;
- st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)&args);
- }
- RSTRING_PTR(str)[0] = '#';
- rb_str_cat2(str, ">");
- return str;
-}
-
-static inline bool
-wmap_live_entry_p(rb_objspace_t *objspace, st_data_t key, st_data_t val)
-{
- return wmap_live_p(objspace, (VALUE)key) && wmap_live_p(objspace, (VALUE)val);
-}
-
-static int
-wmap_each_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
-
- if (wmap_live_entry_p(objspace, key, val)) {
- rb_yield_values(2, (VALUE)key, (VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over keys and objects in a weakly referenced object */
-static VALUE
-wmap_each(VALUE self)
-{
- struct weakmap *w;
- rb_objspace_t *objspace = &rb_objspace;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_i, (st_data_t)objspace);
- return self;
-}
-
-static int
-wmap_each_key_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
-
- if (wmap_live_entry_p(objspace, key, val)) {
- rb_yield((VALUE)key);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over keys and objects in a weakly referenced object */
-static VALUE
-wmap_each_key(VALUE self)
-{
- struct weakmap *w;
- rb_objspace_t *objspace = &rb_objspace;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_key_i, (st_data_t)objspace);
- return self;
-}
-
-static int
-wmap_each_value_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- rb_objspace_t *objspace = (rb_objspace_t *)arg;
-
- if (wmap_live_entry_p(objspace, key, val)) {
- rb_yield((VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over keys and objects in a weakly referenced object */
-static VALUE
-wmap_each_value(VALUE self)
-{
- struct weakmap *w;
- rb_objspace_t *objspace = &rb_objspace;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- st_foreach(w->wmap2obj, wmap_each_value_i, (st_data_t)objspace);
- return self;
-}
-
-static int
-wmap_keys_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
- rb_objspace_t *objspace = argp->objspace;
- VALUE ary = argp->value;
-
- if (wmap_live_entry_p(objspace, key, val)) {
- rb_ary_push(ary, (VALUE)key);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over keys and objects in a weakly referenced object */
-static VALUE
-wmap_keys(VALUE self)
-{
- struct weakmap *w;
- struct wmap_iter_arg args;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- args.objspace = &rb_objspace;
- args.value = rb_ary_new();
- st_foreach(w->wmap2obj, wmap_keys_i, (st_data_t)&args);
- return args.value;
-}
-
-static int
-wmap_values_i(st_data_t key, st_data_t val, st_data_t arg)
-{
- struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
- rb_objspace_t *objspace = argp->objspace;
- VALUE ary = argp->value;
-
- if (wmap_live_entry_p(objspace, key, val)) {
- rb_ary_push(ary, (VALUE)val);
- return ST_CONTINUE;
- }
- else {
- return ST_DELETE;
- }
-}
-
-/* Iterates over values and objects in a weakly referenced object */
-static VALUE
-wmap_values(VALUE self)
-{
- struct weakmap *w;
- struct wmap_iter_arg args;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- args.objspace = &rb_objspace;
- args.value = rb_ary_new();
- st_foreach(w->wmap2obj, wmap_values_i, (st_data_t)&args);
- return args.value;
-}
-
-static int
-wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
-{
- VALUE size, *ptr, *optr;
- if (existing) {
- size = (ptr = optr = (VALUE *)*val)[0];
- ++size;
- SIZED_REALLOC_N(ptr, VALUE, size + 1, size);
- }
- else {
- optr = 0;
- size = 1;
- ptr = ruby_xmalloc0(2 * sizeof(VALUE));
- }
- ptr[0] = size;
- ptr[size] = (VALUE)arg;
- if (ptr == optr) return ST_STOP;
- *val = (st_data_t)ptr;
- return ST_CONTINUE;
-}
-
-/* Creates a weak reference from the given key to the given value */
-static VALUE
-wmap_aset(VALUE self, VALUE key, VALUE value)
-{
- struct weakmap *w;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- if (FL_ABLE(value)) {
- define_final0(value, w->final);
- }
- if (FL_ABLE(key)) {
- define_final0(key, w->final);
- }
-
- st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
- st_insert(w->wmap2obj, (st_data_t)key, (st_data_t)value);
- return nonspecial_obj_id(value);
-}
-
-/* Retrieves a weakly referenced object with the given key */
-static VALUE
-wmap_lookup(VALUE self, VALUE key)
-{
- st_data_t data;
- VALUE obj;
- struct weakmap *w;
- rb_objspace_t *objspace = &rb_objspace;
- GC_ASSERT(wmap_live_p(objspace, key));
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- if (!st_lookup(w->wmap2obj, (st_data_t)key, &data)) return Qundef;
- obj = (VALUE)data;
- if (!wmap_live_p(objspace, obj)) return Qundef;
- return obj;
-}
-
-/* Retrieves a weakly referenced object with the given key */
-static VALUE
-wmap_aref(VALUE self, VALUE key)
-{
- VALUE obj = wmap_lookup(self, key);
- return !UNDEF_P(obj) ? obj : Qnil;
-}
-
-/* Returns +true+ if +key+ is registered */
-static VALUE
-wmap_has_key(VALUE self, VALUE key)
-{
- return RBOOL(!UNDEF_P(wmap_lookup(self, key)));
-}
-
-/* Returns the number of referenced objects */
-static VALUE
-wmap_size(VALUE self)
-{
- struct weakmap *w;
- st_index_t n;
-
- TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
- n = w->wmap2obj->num_entries;
-#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
- return ULONG2NUM(n);
-#else
- return ULL2NUM(n);
-#endif
-}
-
-
-/*
- ------------------------------ WeakKeyMap ------------------------------
-*/
-
-typedef struct weakkeymap_entry {
- VALUE obj;
- st_index_t hash;
-} weakkeymap_entry_t;
-
-struct weakkeymap {
- st_table *map;
- st_table *obj2hash;
- VALUE final;
-};
-
-static int
-weakkeymap_cmp_entry(st_data_t a, st_data_t b)
-{
- struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
- struct weakkeymap_entry *entry_b = (struct weakkeymap_entry *)b;
- if (entry_a == entry_b) {
- return 0;
- }
- else {
- return rb_any_cmp(entry_a->obj, entry_b->obj);
- }
-}
-
-static st_index_t
-weakkeymap_hash_entry(st_data_t a)
-{
- struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
- return entry_a->hash;
-}
-
-static const struct st_hash_type weakkeymap_hash = {
- weakkeymap_cmp_entry,
- weakkeymap_hash_entry,
-};
-
-static void
-wkmap_compact(void *ptr)
-{
- struct weakkeymap *w = ptr;
- if (w->map) rb_gc_update_tbl_refs(w->map);
- w->final = rb_gc_location(w->final);
-}
-
-static void
-wkmap_mark(void *ptr)
-{
- struct weakkeymap *w = ptr;
- rb_mark_tbl_no_pin(w->map);
- rb_gc_mark_movable(w->final);
-}
-
-static void
-wkmap_free(void *ptr)
-{
- struct weakkeymap *w = ptr;
- st_free_table(w->map);
- st_free_table(w->obj2hash);
- xfree(w);
-}
-
-static size_t
-wkmap_memsize(const void *ptr)
-{
- const struct weakkeymap *w = ptr;
- return sizeof(struct weakkeymap) + st_memsize(w->map) + st_memsize(w->obj2hash);
-}
-
-static const rb_data_type_t weakkeymap_type = {
- "weakkeymap",
- {
- wkmap_mark,
- wkmap_free,
- wkmap_memsize,
- wkmap_compact,
- },
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
-};
-
-static VALUE
-wkmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
-{
- struct weakkeymap *w;
- VALUE key;
-
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
-
- /* Get reference from object id. */
- if ((key = id2ref_obj_tbl(&rb_objspace, objid)) == Qundef) {
- rb_bug("wkmap_finalize: objid is not found.");
- }
-
- st_index_t hash;
- if (st_delete(w->obj2hash, (st_data_t *)key, &hash)) {
- weakkeymap_entry_t lookup_entry = {key, hash};
- weakkeymap_entry_t *deleted_entry = NULL;
- if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)deleted_entry)) {
- st_data_t deleted_value;
- st_delete(w->map, (st_data_t *)deleted_entry, &deleted_value);
- xfree(deleted_entry);
- }
- }
-
- return self;
-}
-
-static VALUE
-wkmap_allocate(VALUE klass)
-{
- struct weakkeymap *w;
- VALUE obj = TypedData_Make_Struct(klass, struct weakkeymap, &weakkeymap_type, w);
- w->map = st_init_table(&weakkeymap_hash);
- w->obj2hash = rb_init_identtable();
- w->final = rb_func_lambda_new(wkmap_finalize, obj, 1, 1);
- return obj;
-}
-
-static st_index_t
-wkmap_lookup_hash(struct weakkeymap *w, VALUE key)
-{
- st_index_t hash;
- if (!st_lookup(w->obj2hash, (st_data_t)key, &hash)) {
- hash = rb_any_hash(key);
- }
- return hash;
-}
-
-static weakkeymap_entry_t*
-wkmap_lookup_entry(struct weakkeymap *w, VALUE key, st_index_t hash)
-{
- st_data_t data;
- weakkeymap_entry_t lookup_entry = {key, hash};
-
- if (st_get_key(w->map, (st_data_t)&lookup_entry, &data)) {
- return (weakkeymap_entry_t *)data;
- }
-
- return NULL;
-}
-
-static VALUE
-wkmap_lookup(VALUE self, VALUE key)
-{
- st_data_t data;
- struct weakkeymap *w;
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
-
- st_index_t hash = rb_any_hash(key);
- weakkeymap_entry_t lookup_entry = {key, hash};
-
- if (st_lookup(w->map, (st_data_t)&lookup_entry, &data)) {
- return (VALUE)data;
- }
- return Qundef;
-}
-
-/*
- * call-seq:
- * map[key] -> value
- *
- * Returns the value associated with the given +key+ if found.
- *
- * If +key+ is not found, returns +nil+.
- */
-static VALUE
-wkmap_aref(VALUE self, VALUE key)
-{
- VALUE obj = wkmap_lookup(self, key);
- return obj != Qundef ? obj : Qnil;
-}
-
-/*
- * call-seq:
- * map[key] = value -> value
- *
- * Associates the given +value+ with the given +key+; returns +value+.
- *
- * The reference to +key+ is weak, so when there is no other reference
- * to +key+ it may be garbage collected.
- *
- * If the given +key+ exists, replaces its value with the given +value+;
- * the ordering is not affected
- */
-static VALUE
-wkmap_aset(VALUE self, VALUE key, VALUE value)
-{
- struct weakkeymap *w;
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
-
- if (!(FL_ABLE(key) && !SYMBOL_P(key) && !RB_BIGNUM_TYPE_P(key))) {
- rb_raise(rb_eArgError, "WeakKeyMap must be garbage collectable");
- }
-
- st_index_t hash = wkmap_lookup_hash(w, key);
- weakkeymap_entry_t *key_entry = wkmap_lookup_entry(w, key, hash);
-
- if (!key_entry) {
- key_entry = ALLOC(weakkeymap_entry_t);
- key_entry->obj = key;
- key_entry->hash = hash;
- }
-
- if (!st_insert(w->map, (st_data_t)key_entry, (st_data_t)value)) {
- st_insert(w->obj2hash, (st_data_t)key, (st_data_t)hash);
- define_final0(key, w->final);
- }
-
- return value;
-}
-
-/*
- * call-seq:
- * map.getkey(key) -> existing_key or nil
- *
- * Returns the existing equal key if it exists, otherwise returns +nil+.
- */
-static VALUE
-wkmap_getkey(VALUE self, VALUE key)
-{
- struct weakkeymap *w;
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
-
- st_index_t hash = rb_any_hash(key);
- weakkeymap_entry_t lookup_entry = {key, hash};
-
- weakkeymap_entry_t *key_entry = NULL;
- if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&key_entry)) {
- GC_ASSERT(key_entry != NULL);
-
- VALUE obj = key_entry->obj;
- if (wmap_live_p(&rb_objspace, obj)) {
- return obj;
- }
- }
- return Qnil;
-}
-
-/*
- * call-seq:
- * hash.key?(key) -> true or false
- *
- * Returns +true+ if +key+ is a key in +self+, otherwise +false+.
- */
-static VALUE
-wkmap_has_key(VALUE self, VALUE key)
-{
- return RBOOL(wkmap_lookup(self, key) != Qundef);
-}
-
-/*
- * call-seq:
- * map.clear -> self
- *
- * Removes all map entries; returns +self+.
- */
-static VALUE
-wkmap_clear(VALUE self)
-{
- struct weakkeymap *w;
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
- if (w->map) {
- st_clear(w->map);
- }
- if (w->obj2hash) {
- st_clear(w->obj2hash);
- }
- return self;
-}
-
-/*
- * call-seq:
- * map.inspect -> new_string
- *
- * Returns a new \String containing informations about the map:
-
- * m = ObjectSpace::WeakKeyMap.new
- * m[key] = value
- * m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
- *
- */
-static VALUE
-wkmap_inspect(VALUE self)
-{
- struct weakkeymap *w;
- TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
-
- st_index_t n = 0;
- if (w->map) {
- n = w->map->num_entries;
- }
-
-#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
- const char * format = "#<%"PRIsVALUE":%p size=%lu>";
-#else
- const char * format = "#<%"PRIsVALUE":%p size=%llu>";
-#endif
-
- VALUE str = rb_sprintf(format, rb_class_name(CLASS_OF(self)), (void *)self, n);
- return str;
-}
-
-/*
------------------------------ GC profiler ------------------------------
*/
@@ -14817,25 +14046,6 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
* Finalizer one on 537763480
*/
-/*
- * Document-class: ObjectSpace::WeakMap
- *
- * An ObjectSpace::WeakMap object holds references to
- * any objects, but those objects can get garbage collected.
- *
- * This class is mostly used internally by WeakRef, please use
- * +lib/weakref.rb+ for the public interface.
- */
-
-/*
- * Document-class: ObjectSpace::WeakKeyMap
- *
- * An ObjectSpace::WeakKeyMap object holds references to
- * any objects, but objects uses as keys can be garbage collected.
- *
- * Objects used as values can't be garbage collected until the key is.
- */
-
/* Document-class: GC::Profiler
*
* The GC profiler provides access to information on GC runs including time,
@@ -14925,37 +14135,6 @@ Init_GC(void)
rb_define_module_function(rb_mObjSpace, "count_objects", count_objects, -1);
- {
- VALUE rb_cWeakMap = rb_define_class_under(rb_mObjSpace, "WeakMap", rb_cObject);
- rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
- rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
- rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
- rb_define_method(rb_cWeakMap, "include?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "member?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "key?", wmap_has_key, 1);
- rb_define_method(rb_cWeakMap, "inspect", wmap_inspect, 0);
- rb_define_method(rb_cWeakMap, "each", wmap_each, 0);
- rb_define_method(rb_cWeakMap, "each_pair", wmap_each, 0);
- rb_define_method(rb_cWeakMap, "each_key", wmap_each_key, 0);
- rb_define_method(rb_cWeakMap, "each_value", wmap_each_value, 0);
- rb_define_method(rb_cWeakMap, "keys", wmap_keys, 0);
- rb_define_method(rb_cWeakMap, "values", wmap_values, 0);
- rb_define_method(rb_cWeakMap, "size", wmap_size, 0);
- rb_define_method(rb_cWeakMap, "length", wmap_size, 0);
- rb_include_module(rb_cWeakMap, rb_mEnumerable);
- }
-
- {
- VALUE rb_cWeakKeyMap = rb_define_class_under(rb_mObjSpace, "WeakKeyMap", rb_cObject);
- rb_define_alloc_func(rb_cWeakKeyMap, wkmap_allocate);
- rb_define_method(rb_cWeakKeyMap, "[]=", wkmap_aset, 2);
- rb_define_method(rb_cWeakKeyMap, "[]", wkmap_aref, 1);
- rb_define_method(rb_cWeakKeyMap, "getkey", wkmap_getkey, 1);
- rb_define_method(rb_cWeakKeyMap, "key?", wkmap_has_key, 1);
- rb_define_method(rb_cWeakKeyMap, "clear", wkmap_clear, 0);
- rb_define_method(rb_cWeakKeyMap, "inspect", wkmap_inspect, 0);
- }
-
/* internal methods */
rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency_m, 0);
rb_define_singleton_method(rb_mGC, "verify_transient_heap_internal_consistency", gc_verify_transient_heap_internal_consistency, 0);
diff --git a/inits.c b/inits.c
index f41103ca26..8b66334a4d 100644
--- a/inits.c
+++ b/inits.c
@@ -62,6 +62,7 @@ rb_call_inits(void)
CALL(Binding);
CALL(Math);
CALL(GC);
+ CALL(WeakMap);
CALL(Enumerator);
CALL(Ractor);
CALL(VM);
diff --git a/internal/gc.h b/internal/gc.h
index ad0ceac7de..73b6e6fed0 100644
--- a/internal/gc.h
+++ b/internal/gc.h
@@ -243,6 +243,9 @@ void rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache);
size_t rb_gc_obj_slot_size(VALUE obj);
bool rb_gc_size_allocatable_p(size_t size);
int rb_objspace_garbage_object_p(VALUE obj);
+bool rb_gc_is_ptr_to_obj(void *ptr);
+VALUE rb_gc_id2ref_obj_tbl(VALUE objid);
+VALUE rb_define_finalizer_no_check(VALUE obj, VALUE block);
void rb_gc_mark_and_move(VALUE *ptr);
diff --git a/weakmap.c b/weakmap.c
new file mode 100644
index 0000000000..6915b3c71d
--- /dev/null
+++ b/weakmap.c
@@ -0,0 +1,800 @@
+#include "internal.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
+#include "internal/proc.h"
+#include "internal/sanitizers.h"
+#include "ruby/st.h"
+#include "ruby/st.h"
+
+struct weakmap {
+ st_table *obj2wmap; /* obj -> [ref,...] */
+ st_table *wmap2obj; /* ref -> obj */
+ VALUE final;
+};
+
+static void
+wmap_compact(void *ptr)
+{
+ struct weakmap *w = ptr;
+ if (w->wmap2obj) rb_gc_update_tbl_refs(w->wmap2obj);
+ if (w->obj2wmap) rb_gc_update_tbl_refs(w->obj2wmap);
+ w->final = rb_gc_location(w->final);
+}
+
+static void
+wmap_mark(void *ptr)
+{
+ struct weakmap *w = ptr;
+ rb_gc_mark_movable(w->final);
+}
+
+static int
+wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
+{
+ VALUE *ptr = (VALUE *)val;
+ ruby_sized_xfree(ptr, (ptr[0] + 1) * sizeof(VALUE));
+ return ST_CONTINUE;
+}
+
+static void
+wmap_free(void *ptr)
+{
+ struct weakmap *w = ptr;
+ st_foreach(w->obj2wmap, wmap_free_map, 0);
+ st_free_table(w->obj2wmap);
+ st_free_table(w->wmap2obj);
+ xfree(w);
+}
+
+static int
+wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
+{
+ VALUE *ptr = (VALUE *)val;
+ *(size_t *)arg += (ptr[0] + 1) * sizeof(VALUE);
+ return ST_CONTINUE;
+}
+
+static size_t
+wmap_memsize(const void *ptr)
+{
+ size_t size;
+ const struct weakmap *w = ptr;
+ size = sizeof(*w);
+ size += st_memsize(w->obj2wmap);
+ size += st_memsize(w->wmap2obj);
+ st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
+ return size;
+}
+
+static const rb_data_type_t weakmap_type = {
+ "weakmap",
+ {
+ wmap_mark,
+ wmap_free,
+ wmap_memsize,
+ wmap_compact,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self));
+
+static VALUE
+wmap_allocate(VALUE klass)
+{
+ struct weakmap *w;
+ VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
+ w->obj2wmap = rb_init_identtable();
+ w->wmap2obj = rb_init_identtable();
+ w->final = rb_func_lambda_new(wmap_finalize, obj, 1, 1);
+ return obj;
+}
+
+static int
+wmap_live_p(VALUE obj)
+{
+ if (SPECIAL_CONST_P(obj)) return TRUE;
+ /* If rb_gc_is_ptr_to_obj returns false, the page could be in the tomb heap
+ * or have already been freed. */
+ if (!rb_gc_is_ptr_to_obj((void *)obj)) return FALSE;
+
+ void *poisoned = asan_poisoned_object_p(obj);
+ asan_unpoison_object(obj, false);
+
+ enum ruby_value_type t = BUILTIN_TYPE(obj);
+ int ret = (!(t == T_NONE || t >= T_FIXNUM || t == T_ICLASS) &&
+ !rb_objspace_garbage_object_p(obj));
+
+ if (poisoned) {
+ asan_poison_object(obj);
+ }
+
+ return ret;
+}
+
+static int
+wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+{
+ VALUE wmap, *ptr, size, i, j;
+ if (!existing) return ST_STOP;
+ wmap = (VALUE)arg, ptr = (VALUE *)*value;
+ for (i = j = 1, size = ptr[0]; i <= size; ++i) {
+ if (ptr[i] != wmap) {
+ ptr[j++] = ptr[i];
+ }
+ }
+ if (j == 1) {
+ ruby_sized_xfree(ptr, i * sizeof(VALUE));
+ return ST_DELETE;
+ }
+ if (j < i) {
+ SIZED_REALLOC_N(ptr, VALUE, j + 1, i);
+ ptr[0] = j;
+ *value = (st_data_t)ptr;
+ }
+ return ST_CONTINUE;
+}
+
+/* :nodoc: */
+static VALUE
+wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
+{
+ st_data_t orig, wmap, data;
+ VALUE obj, *rids, i, size;
+ struct weakmap *w;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ /* Get reference from object id. */
+ if (UNDEF_P(obj = rb_gc_id2ref_obj_tbl(objid))) {
+ rb_bug("wmap_finalize: objid is not found.");
+ }
+
+ /* obj is original referenced object and/or weak reference. */
+ orig = (st_data_t)obj;
+ if (st_delete(w->obj2wmap, &orig, &data)) {
+ rids = (VALUE *)data;
+ size = *rids++;
+ for (i = 0; i < size; ++i) {
+ wmap = (st_data_t)rids[i];
+ st_delete(w->wmap2obj, &wmap, NULL);
+ }
+ ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
+ }
+
+ wmap = (st_data_t)obj;
+ if (st_delete(w->wmap2obj, &wmap, &orig)) {
+ wmap = (st_data_t)obj;
+ st_update(w->obj2wmap, orig, wmap_final_func, wmap);
+ }
+ return self;
+}
+
+static VALUE
+wmap_inspect_append(VALUE str, VALUE obj)
+{
+ if (SPECIAL_CONST_P(obj)) {
+ return rb_str_append(str, rb_inspect(obj));
+ }
+ else if (wmap_live_p(obj)) {
+ return rb_str_append(str, rb_any_to_s(obj));
+ }
+ else {
+ return rb_str_catf(str, "#<collected:%p>", (void*)obj);
+ }
+}
+
+static int
+wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ VALUE str = (VALUE)arg;
+ VALUE k = (VALUE)key, v = (VALUE)val;
+
+ if (RSTRING_PTR(str)[0] == '#') {
+ rb_str_cat2(str, ", ");
+ }
+ else {
+ rb_str_cat2(str, ": ");
+ RSTRING_PTR(str)[0] = '#';
+ }
+ wmap_inspect_append(str, k);
+ rb_str_cat2(str, " => ");
+ wmap_inspect_append(str, v);
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+wmap_inspect(VALUE self)
+{
+ VALUE c = rb_class_name(CLASS_OF(self));
+ struct weakmap *w;
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+
+ VALUE str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
+ if (w->wmap2obj) {
+ st_foreach(w->wmap2obj, wmap_inspect_i, (st_data_t)str);
+ }
+ RSTRING_PTR(str)[0] = '#';
+ rb_str_cat2(str, ">");
+ return str;
+}
+
+static inline bool
+wmap_live_entry_p(st_data_t key, st_data_t val)
+{
+ return wmap_live_p((VALUE)key) && wmap_live_p((VALUE)val);
+}
+
+static int
+wmap_each_i(st_data_t key, st_data_t val, st_data_t _)
+{
+ if (wmap_live_entry_p(key, val)) {
+ rb_yield_values(2, (VALUE)key, (VALUE)val);
+ return ST_CONTINUE;
+ }
+ else {
+ return ST_DELETE;
+ }
+}
+
+/* Iterates over keys and objects in a weakly referenced object */
+static VALUE
+wmap_each(VALUE self)
+{
+ struct weakmap *w;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ st_foreach(w->wmap2obj, wmap_each_i, (st_data_t)0);
+ return self;
+}
+
+static int
+wmap_each_key_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ if (wmap_live_entry_p(key, val)) {
+ rb_yield((VALUE)key);
+ return ST_CONTINUE;
+ }
+ else {
+ return ST_DELETE;
+ }
+}
+
+/* Iterates over keys and objects in a weakly referenced object */
+static VALUE
+wmap_each_key(VALUE self)
+{
+ struct weakmap *w;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ st_foreach(w->wmap2obj, wmap_each_key_i, (st_data_t)0);
+ return self;
+}
+
+static int
+wmap_each_value_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ if (wmap_live_entry_p(key, val)) {
+ rb_yield((VALUE)val);
+ return ST_CONTINUE;
+ }
+ else {
+ return ST_DELETE;
+ }
+}
+
+/* Iterates over keys and objects in a weakly referenced object */
+static VALUE
+wmap_each_value(VALUE self)
+{
+ struct weakmap *w;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ st_foreach(w->wmap2obj, wmap_each_value_i, (st_data_t)0);
+ return self;
+}
+
+static int
+wmap_keys_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ VALUE ary = (VALUE)arg;
+
+ if (wmap_live_entry_p(key, val)) {
+ rb_ary_push(ary, (VALUE)key);
+ return ST_CONTINUE;
+ }
+ else {
+ return ST_DELETE;
+ }
+}
+
+/* Iterates over keys and objects in a weakly referenced object */
+static VALUE
+wmap_keys(VALUE self)
+{
+ struct weakmap *w;
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+
+ VALUE ary = rb_ary_new();
+ st_foreach(w->wmap2obj, wmap_keys_i, (st_data_t)ary);
+ return ary;
+}
+
+static int
+wmap_values_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ VALUE ary = (VALUE)arg;
+
+ if (wmap_live_entry_p(key, val)) {
+ rb_ary_push(ary, (VALUE)val);
+ return ST_CONTINUE;
+ }
+ else {
+ return ST_DELETE;
+ }
+}
+
+/* Iterates over values and objects in a weakly referenced object */
+static VALUE
+wmap_values(VALUE self)
+{
+ struct weakmap *w;
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+
+ VALUE ary = rb_ary_new();
+ st_foreach(w->wmap2obj, wmap_values_i, (st_data_t)ary);
+ return ary;
+}
+
+static int
+wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
+{
+ VALUE size, *ptr, *optr;
+ if (existing) {
+ size = (ptr = optr = (VALUE *)*val)[0];
+ ++size;
+ SIZED_REALLOC_N(ptr, VALUE, size + 1, size);
+ }
+ else {
+ optr = 0;
+ size = 1;
+ ptr = ruby_xmalloc(2 * sizeof(VALUE));
+ }
+ ptr[0] = size;
+ ptr[size] = (VALUE)arg;
+ if (ptr == optr) return ST_STOP;
+ *val = (st_data_t)ptr;
+ return ST_CONTINUE;
+}
+
+static VALUE
+nonspecial_obj_id(VALUE obj)
+{
+#if SIZEOF_LONG == SIZEOF_VOIDP
+ return (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG);
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+ return LL2NUM((SIGNED_VALUE)(obj) / 2);
+#else
+# error not supported
+#endif
+}
+
+/* Creates a weak reference from the given key to the given value */
+static VALUE
+wmap_aset(VALUE self, VALUE key, VALUE value)
+{
+ struct weakmap *w;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ if (FL_ABLE(value)) {
+ rb_define_finalizer_no_check(value, w->final);
+ }
+ if (FL_ABLE(key)) {
+ rb_define_finalizer_no_check(key, w->final);
+ }
+
+ st_update(w->obj2wmap, (st_data_t)value, wmap_aset_update, key);
+ st_insert(w->wmap2obj, (st_data_t)key, (st_data_t)value);
+ return nonspecial_obj_id(value);
+}
+
+/* Retrieves a weakly referenced object with the given key */
+static VALUE
+wmap_lookup(VALUE self, VALUE key)
+{
+ assert(wmap_live_p(key));
+
+ struct weakmap *w;
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+
+ st_data_t data;
+ if (!st_lookup(w->wmap2obj, (st_data_t)key, &data)) return Qundef;
+
+ VALUE obj = (VALUE)data;
+ if (!wmap_live_p(obj)) return Qundef;
+ return obj;
+}
+
+/* Retrieves a weakly referenced object with the given key */
+static VALUE
+wmap_aref(VALUE self, VALUE key)
+{
+ VALUE obj = wmap_lookup(self, key);
+ return !UNDEF_P(obj) ? obj : Qnil;
+}
+
+/* Returns +true+ if +key+ is registered */
+static VALUE
+wmap_has_key(VALUE self, VALUE key)
+{
+ return RBOOL(!UNDEF_P(wmap_lookup(self, key)));
+}
+
+/* Returns the number of referenced objects */
+static VALUE
+wmap_size(VALUE self)
+{
+ struct weakmap *w;
+ st_index_t n;
+
+ TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
+ n = w->wmap2obj->num_entries;
+#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
+ return ULONG2NUM(n);
+#else
+ return ULL2NUM(n);
+#endif
+}
+
+typedef struct weakkeymap_entry {
+ VALUE obj;
+ st_index_t hash;
+} weakkeymap_entry_t;
+
+struct weakkeymap {
+ st_table *map;
+ st_table *obj2hash;
+ VALUE final;
+};
+
+static int
+weakkeymap_cmp_entry(st_data_t a, st_data_t b)
+{
+ struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
+ struct weakkeymap_entry *entry_b = (struct weakkeymap_entry *)b;
+ if (entry_a == entry_b) {
+ return 0;
+ }
+ else {
+ return rb_any_cmp(entry_a->obj, entry_b->obj);
+ }
+}
+
+static st_index_t
+weakkeymap_hash_entry(st_data_t a)
+{
+ struct weakkeymap_entry *entry_a = (struct weakkeymap_entry *)a;
+ return entry_a->hash;
+}
+
+static const struct st_hash_type weakkeymap_hash = {
+ weakkeymap_cmp_entry,
+ weakkeymap_hash_entry,
+};
+
+static void
+wkmap_compact(void *ptr)
+{
+ struct weakkeymap *w = ptr;
+ if (w->map) rb_gc_update_tbl_refs(w->map);
+ w->final = rb_gc_location(w->final);
+}
+
+static void
+wkmap_mark(void *ptr)
+{
+ struct weakkeymap *w = ptr;
+ rb_mark_tbl_no_pin(w->map);
+ rb_gc_mark_movable(w->final);
+}
+
+static void
+wkmap_free(void *ptr)
+{
+ struct weakkeymap *w = ptr;
+ st_free_table(w->map);
+ st_free_table(w->obj2hash);
+ xfree(w);
+}
+
+static size_t
+wkmap_memsize(const void *ptr)
+{
+ const struct weakkeymap *w = ptr;
+ return sizeof(struct weakkeymap) + st_memsize(w->map) + st_memsize(w->obj2hash);
+}
+
+static const rb_data_type_t weakkeymap_type = {
+ "weakkeymap",
+ {
+ wkmap_mark,
+ wkmap_free,
+ wkmap_memsize,
+ wkmap_compact,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+static VALUE
+wkmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
+{
+ struct weakkeymap *w;
+ VALUE key;
+
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+
+ /* Get reference from object id. */
+ if ((key = rb_gc_id2ref_obj_tbl(objid)) == Qundef) {
+ rb_bug("wkmap_finalize: objid is not found.");
+ }
+
+ st_index_t hash;
+ if (st_delete(w->obj2hash, (st_data_t *)key, &hash)) {
+ weakkeymap_entry_t lookup_entry = {key, hash};
+ weakkeymap_entry_t *deleted_entry = NULL;
+ if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)deleted_entry)) {
+ st_data_t deleted_value;
+ st_delete(w->map, (st_data_t *)deleted_entry, &deleted_value);
+ xfree(deleted_entry);
+ }
+ }
+
+ return self;
+}
+
+static VALUE
+wkmap_allocate(VALUE klass)
+{
+ struct weakkeymap *w;
+ VALUE obj = TypedData_Make_Struct(klass, struct weakkeymap, &weakkeymap_type, w);
+ w->map = st_init_table(&weakkeymap_hash);
+ w->obj2hash = rb_init_identtable();
+ w->final = rb_func_lambda_new(wkmap_finalize, obj, 1, 1);
+ return obj;
+}
+
+static st_index_t
+wkmap_lookup_hash(struct weakkeymap *w, VALUE key)
+{
+ st_index_t hash;
+ if (!st_lookup(w->obj2hash, (st_data_t)key, &hash)) {
+ hash = rb_any_hash(key);
+ }
+ return hash;
+}
+
+static weakkeymap_entry_t*
+wkmap_lookup_entry(struct weakkeymap *w, VALUE key, st_index_t hash)
+{
+ st_data_t data;
+ weakkeymap_entry_t lookup_entry = {key, hash};
+
+ if (st_get_key(w->map, (st_data_t)&lookup_entry, &data)) {
+ return (weakkeymap_entry_t *)data;
+ }
+
+ return NULL;
+}
+
+static VALUE
+wkmap_lookup(VALUE self, VALUE key)
+{
+ st_data_t data;
+ struct weakkeymap *w;
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+
+ st_index_t hash = rb_any_hash(key);
+ weakkeymap_entry_t lookup_entry = {key, hash};
+
+ if (st_lookup(w->map, (st_data_t)&lookup_entry, &data)) {
+ return (VALUE)data;
+ }
+ return Qundef;
+}
+
+/*
+ * call-seq:
+ * map[key] -> value
+ *
+ * Returns the value associated with the given +key+ if found.
+ *
+ * If +key+ is not found, returns +nil+.
+ */
+static VALUE
+wkmap_aref(VALUE self, VALUE key)
+{
+ VALUE obj = wkmap_lookup(self, key);
+ return obj != Qundef ? obj : Qnil;
+}
+
+/*
+ * call-seq:
+ * map[key] = value -> value
+ *
+ * Associates the given +value+ with the given +key+; returns +value+.
+ *
+ * The reference to +key+ is weak, so when there is no other reference
+ * to +key+ it may be garbage collected.
+ *
+ * If the given +key+ exists, replaces its value with the given +value+;
+ * the ordering is not affected
+ */
+static VALUE
+wkmap_aset(VALUE self, VALUE key, VALUE value)
+{
+ struct weakkeymap *w;
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+
+ if (!(FL_ABLE(key) && !SYMBOL_P(key) && !RB_BIGNUM_TYPE_P(key))) {
+ rb_raise(rb_eArgError, "WeakKeyMap must be garbage collectable");
+ }
+
+ st_index_t hash = wkmap_lookup_hash(w, key);
+ weakkeymap_entry_t *key_entry = wkmap_lookup_entry(w, key, hash);
+
+ if (!key_entry) {
+ key_entry = ALLOC(weakkeymap_entry_t);
+ key_entry->obj = key;
+ key_entry->hash = hash;
+ }
+
+ if (!st_insert(w->map, (st_data_t)key_entry, (st_data_t)value)) {
+ st_insert(w->obj2hash, (st_data_t)key, (st_data_t)hash);
+ rb_define_finalizer_no_check(key, w->final);
+ }
+
+ return value;
+}
+
+/*
+ * call-seq:
+ * map.getkey(key) -> existing_key or nil
+ *
+ * Returns the existing equal key if it exists, otherwise returns +nil+.
+ */
+static VALUE
+wkmap_getkey(VALUE self, VALUE key)
+{
+ struct weakkeymap *w;
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+
+ st_index_t hash = rb_any_hash(key);
+ weakkeymap_entry_t lookup_entry = {key, hash};
+
+ weakkeymap_entry_t *key_entry = NULL;
+ if (st_get_key(w->map, (st_data_t)&lookup_entry, (st_data_t *)&key_entry)) {
+ assert(key_entry != NULL);
+
+ VALUE obj = key_entry->obj;
+ if (wmap_live_p(obj)) {
+ return obj;
+ }
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * hash.key?(key) -> true or false
+ *
+ * Returns +true+ if +key+ is a key in +self+, otherwise +false+.
+ */
+static VALUE
+wkmap_has_key(VALUE self, VALUE key)
+{
+ return RBOOL(wkmap_lookup(self, key) != Qundef);
+}
+
+/*
+ * call-seq:
+ * map.clear -> self
+ *
+ * Removes all map entries; returns +self+.
+ */
+static VALUE
+wkmap_clear(VALUE self)
+{
+ struct weakkeymap *w;
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+ if (w->map) {
+ st_clear(w->map);
+ }
+ if (w->obj2hash) {
+ st_clear(w->obj2hash);
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * map.inspect -> new_string
+ *
+ * Returns a new \String containing informations about the map:
+
+ * m = ObjectSpace::WeakKeyMap.new
+ * m[key] = value
+ * m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
+ *
+ */
+static VALUE
+wkmap_inspect(VALUE self)
+{
+ struct weakkeymap *w;
+ TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
+
+ st_index_t n = 0;
+ if (w->map) {
+ n = w->map->num_entries;
+ }
+
+#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
+ const char * format = "#<%"PRIsVALUE":%p size=%lu>";
+#else
+ const char * format = "#<%"PRIsVALUE":%p size=%llu>";
+#endif
+
+ VALUE str = rb_sprintf(format, rb_class_name(CLASS_OF(self)), (void *)self, n);
+ return str;
+}
+
+/*
+ * Document-class: ObjectSpace::WeakMap
+ *
+ * An ObjectSpace::WeakMap object holds references to
+ * any objects, but those objects can get garbage collected.
+ *
+ * This class is mostly used internally by WeakRef, please use
+ * +lib/weakref.rb+ for the public interface.
+ */
+
+/*
+ * Document-class: ObjectSpace::WeakKeyMap
+ *
+ * An ObjectSpace::WeakKeyMap object holds references to
+ * any objects, but objects uses as keys can be garbage collected.
+ *
+ * Objects used as values can't be garbage collected until the key is.
+ */
+
+void
+Init_WeakMap(void)
+{
+ VALUE rb_mObjectSpace = rb_define_module("ObjectSpace");
+
+ VALUE rb_cWeakMap = rb_define_class_under(rb_mObjectSpace, "WeakMap", rb_cObject);
+ rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
+ rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
+ rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
+ rb_define_method(rb_cWeakMap, "include?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "member?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "key?", wmap_has_key, 1);
+ rb_define_method(rb_cWeakMap, "inspect", wmap_inspect, 0);
+ rb_define_method(rb_cWeakMap, "each", wmap_each, 0);
+ rb_define_method(rb_cWeakMap, "each_pair", wmap_each, 0);
+ rb_define_method(rb_cWeakMap, "each_key", wmap_each_key, 0);
+ rb_define_method(rb_cWeakMap, "each_value", wmap_each_value, 0);
+ rb_define_method(rb_cWeakMap, "keys", wmap_keys, 0);
+ rb_define_method(rb_cWeakMap, "values", wmap_values, 0);
+ rb_define_method(rb_cWeakMap, "size", wmap_size, 0);
+ rb_define_method(rb_cWeakMap, "length", wmap_size, 0);
+ rb_include_module(rb_cWeakMap, rb_mEnumerable);
+
+ VALUE rb_cWeakKeyMap = rb_define_class_under(rb_mObjectSpace, "WeakKeyMap", rb_cObject);
+ rb_define_alloc_func(rb_cWeakKeyMap, wkmap_allocate);
+ rb_define_method(rb_cWeakKeyMap, "[]=", wkmap_aset, 2);
+ rb_define_method(rb_cWeakKeyMap, "[]", wkmap_aref, 1);
+ rb_define_method(rb_cWeakKeyMap, "getkey", wkmap_getkey, 1);
+ rb_define_method(rb_cWeakKeyMap, "key?", wkmap_has_key, 1);
+ rb_define_method(rb_cWeakKeyMap, "clear", wkmap_clear, 0);
+ rb_define_method(rb_cWeakKeyMap, "inspect", wkmap_inspect, 0);
+}