Skip to content

Commit

Permalink
Add ObjectSpace::WeakKeyMap
Browse files Browse the repository at this point in the history
  • Loading branch information
soutaro committed Dec 13, 2023
1 parent 5fe696d commit bb13311
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 0 deletions.
101 changes: 101 additions & 0 deletions core/object_space/weak_key_map.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module ObjectSpace
# <!-- rdoc-file=weakmap.c -->
# 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.
#
class WeakKeyMap[Key, Value]
public

# <!--
# rdoc-file=weakmap.c
# - map[key] -> value
# -->
# Returns the value associated with the given `key` if found.
#
# If `key` is not found, returns `nil`.
#
def []: (Key) -> Value?

# <!--
# rdoc-file=weakmap.c
# - 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
#
def []=: (Key, Value?) -> Value?

# <!--
# rdoc-file=weakmap.c
# - map.clear -> self
# -->
# Removes all map entries; returns `self`.
#
def clear: () -> self

# <!--
# rdoc-file=weakmap.c
# - map.delete(key) -> value or nil
# - map.delete(key) {|key| ... } -> object
# -->
# Deletes the entry for the given `key` and returns its associated value.
#
# If no block is given and `key` is found, deletes the entry and returns the
# associated value:
# m = ObjectSpace::WeakKeyMap.new
# m["foo"] = 1
# m.delete("foo") # => 1
# m["foo"] # => nil
#
# If no block given and `key` is not found, returns `nil`.
#
# If a block is given and `key` is found, ignores the block, deletes the entry,
# and returns the associated value:
# m = ObjectSpace::WeakKeyMap.new
# m["foo"] = 2
# h.delete("foo") { |key| raise 'Will never happen'} # => 2
#
# If a block is given and `key` is not found, calls the block and returns the
# block's return value:
# m = ObjectSpace::WeakKeyMap.new
# h.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
#
def delete: (Key) -> Value?
| [T] (Key) { (Key) -> T } -> (Value | T)

# <!--
# rdoc-file=weakmap.c
# - map.getkey(key) -> existing_key or nil
# -->
# Returns the existing equal key if it exists, otherwise returns `nil`.
#
def getkey: (untyped) -> Key?

# <!--
# rdoc-file=weakmap.c
# - 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>"
#
def inspect: () -> String

# <!--
# rdoc-file=weakmap.c
# - hash.key?(key) -> true or false
# -->
# Returns `true` if `key` is a key in `self`, otherwise `false`.
#
def key?: (Key) -> bool
end
end
109 changes: 109 additions & 0 deletions test/stdlib/ObjectSpace_WeakKeyMap_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require_relative "test_helper"
require 'objspace'

class ObjectSpace_WeakKeyMapTest < Test::Unit::TestCase
include TestHelper

testing "::ObjectSpace::WeakKeyMap[::String, ::Integer]"

def test_aref
map = ObjectSpace::WeakKeyMap.new()

map["foo"] = 123

assert_send_type(
"(::String) -> ::Integer",
map, :[], "foo"
)

assert_send_type(
"(::String) -> nil",
map, :[], "bar"
)
end

def test_aref_update
map = ObjectSpace::WeakKeyMap.new()

assert_send_type(
"(::String, ::Integer) -> ::Integer",
map, :[]=, "foo", 123
)

assert_send_type(
"(::String, nil) -> nil",
map, :[]=, "bar", nil
)
end

def test_clear
map = ObjectSpace::WeakKeyMap.new()

assert_send_type(
"() -> ::ObjectSpace::WeakKeyMap",
map, :clear
)
end

def test_delete
map = ObjectSpace::WeakKeyMap.new()

map["foo"] = 123
map["bar"] = 123

assert_send_type(
"(::String) -> ::Integer",
map, :delete, "foo"
)
assert_send_type(
"(::String) -> nil",
map, :delete, "foo"
)

assert_send_type(
"(::String) { (::String) -> nil } -> ::Integer",
map, :delete, "bar", &proc { nil }
)
assert_send_type(
"(::String) { (::String) -> ::String } -> ::String",
map, :delete, "bar", &proc { "Hello" }
)
end

def test_getkey
map = ObjectSpace::WeakKeyMap.new()

map["foo"] = 123

assert_send_type(
"(::Integer) -> ::String",
map, :getkey, 123
)
assert_send_type(
"(::String) -> nil",
map, :getkey, "abc"
)
end

def test_inspect
map = ObjectSpace::WeakKeyMap.new()

map["foo"] = 123

assert_send_type(
"() -> ::String",
map, :inspect
)
end

def test_key?
map = ObjectSpace::WeakKeyMap.new()

map["foo"] = 123

assert_send_type(
"(::String) -> bool",
map, :key?, "foo"
)
end
end

0 comments on commit bb13311

Please sign in to comment.