From a828d3710980ead6616b25cc4a6090398b8bc18a Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 16 Nov 2020 23:40:56 +0900 Subject: [PATCH] Add built-in ObjectSpace definition https://ruby-doc.org/core-2.7.2/ObjectSpace.html --- core/object_space.rbs | 98 +++++++++++++++++++++++++++++++++ test/stdlib/ObjectSpace_test.rb | 58 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 core/object_space.rbs create mode 100644 test/stdlib/ObjectSpace_test.rb diff --git a/core/object_space.rbs b/core/object_space.rbs new file mode 100644 index 000000000..f623e6b30 --- /dev/null +++ b/core/object_space.rbs @@ -0,0 +1,98 @@ +# The ObjectSpace module contains a number of routines that interact with the +# garbage collection facility and allow you to traverse all living objects with +# an iterator. +# +# ObjectSpace also provides support for object finalizers, procs that will be +# called when a specific object is about to be destroyed by garbage collection. +# +# require 'objspace' +# +# a = "A" +# b = "B" +# +# ObjectSpace.define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" }) +# ObjectSpace.define_finalizer(b, proc {|id| puts "Finalizer two on #{id}" }) +# +# *produces:* +# +# Finalizer two on 537763470 +# Finalizer one on 537763480 +module ObjectSpace + def self._id2ref: (Integer id) -> untyped + + # Counts all objects grouped by type. + # + # It returns a hash, such as: + # { + # :TOTAL=>10000, + # :FREE=>3011, + # :T_OBJECT=>6, + # :T_CLASS=>404, + # # ... + # } + # + # The contents of the returned hash are implementation specific. It may be + # changed in future. + # + # The keys starting with `:T_` means live objects. For example, `:T_ARRAY` is + # the number of arrays. `:FREE` means object slots which is not used now. + # `:TOTAL` means sum of above. + # + # If the optional argument `result_hash` is given, it is overwritten and + # returned. This is intended to avoid probe effect. + # + # h = {} + # ObjectSpace.count_objects(h) + # puts h + # # => { :TOTAL=>10000, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249 } + # + # This method is only expected to work on C Ruby. + # + def self.count_objects: (?Hash[Symbol, Integer] result_hash) -> Hash[Symbol, Integer] + + # Adds *aProc* as a finalizer, to be called after *obj* was destroyed. The + # object ID of the *obj* will be passed as an argument to *aProc*. If *aProc* is + # a lambda or method, make sure it can be called with a single argument. + # + def self.define_finalizer: (untyped obj, ^(Integer id) -> void aProc) -> [ Integer, Proc ] + | (untyped obj) { (Integer id) -> void } -> [ Integer, Proc ] + + # Calls the block once for each living, nonimmediate object in this Ruby + # process. If *module* is specified, calls the block for only those classes or + # modules that match (or are a subclass of) *module*. Returns the number of + # objects found. Immediate objects (`Fixnum`s, `Symbol`s `true`, `false`, and + # `nil`) are never returned. In the example below, #each_object returns both the + # numbers we defined and several constants defined in the Math module. + # + # If no block is given, an enumerator is returned instead. + # + # a = 102.7 + # b = 95 # Won't be returned + # c = 12345678987654321 + # count = ObjectSpace.each_object(Numeric) {|x| p x } + # puts "Total count: #{count}" + # + # *produces:* + # + # 12345678987654321 + # 102.7 + # 2.71828182845905 + # 3.14159265358979 + # 2.22044604925031e-16 + # 1.7976931348623157e+308 + # 2.2250738585072e-308 + # Total count: 7 + # + def self.each_object: (?Module `module`) -> Enumerator[untyped, Integer] + | (?Module `module`) { (untyped obj) -> void } -> Integer + + def self.garbage_collect: (?full_mark: bool, ?immediate_mark: bool, ?immediate_sweep: bool) -> void + + # Removes all finalizers for *obj*. + # + def self.undefine_finalizer: [X] (X obj) -> X + + private + + def garbage_collect: (?full_mark: bool, ?immediate_mark: bool, ?immediate_sweep: bool) -> void +end diff --git a/test/stdlib/ObjectSpace_test.rb b/test/stdlib/ObjectSpace_test.rb new file mode 100644 index 000000000..0e76457b9 --- /dev/null +++ b/test/stdlib/ObjectSpace_test.rb @@ -0,0 +1,58 @@ +require_relative "test_helper" + +class ObjectSpaceTest < Minitest::Test + include TypeAssertions + + testing "singleton(::ObjectSpace)" + + def test__id2ref + assert_send_type "(Integer) -> top", + ObjectSpace, :_id2ref, 198 + end + + def test_count_objects + assert_send_type "() -> Hash[Symbol, Integer]", + ObjectSpace, :count_objects + assert_send_type "(Hash[Symbol, Integer]) -> Hash[Symbol, Integer]", + ObjectSpace, :count_objects, {} + assert_send_type "(Hash[Symbol, Integer]) -> Hash[Symbol, Integer]", + ObjectSpace, :count_objects, { TOTAL: 0 } + end + + def test_define_finalizer + assert_send_type "(top, ^(Integer) -> void) -> [Integer, Proc]", + ObjectSpace, :define_finalizer, "abc", ->(id) { "id: #{id}" } + assert_send_type "(top) { (Integer) -> void } -> [Integer, Proc]", + ObjectSpace, :define_finalizer, "abc" do |id| "id: #{id}" end + end + + def test_each_object + klass = Class.new + klass.new + + # NOTE: Commented out because they're too slow. + # assert_send_type "() -> Enumerator[top, Integer]", + # ObjectSpace, :each_object + # assert_send_type "() { (top) -> void } -> Integer", + # ObjectSpace, :each_object do |obj| obj.to_s end + + assert_send_type "(Module) -> Enumerator[top, Integer]", + ObjectSpace, :each_object, klass + assert_send_type "(Module) { (top) -> void } -> Integer", + ObjectSpace, :each_object, klass do |obj| obj.to_s end + end + + def test_garbage_collect + assert_send_type "() -> void", + ObjectSpace, :garbage_collect + assert_send_type "(full_mark: bool, immediate_mark: bool, immediate_sweep: bool) -> void", + ObjectSpace, :garbage_collect, full_mark: false, immediate_mark: false, immediate_sweep: false + end + + def test_undefine_finalizer + assert_send_type "(String) -> String", + ObjectSpace, :undefine_finalizer, "abc" + assert_send_type "(Array) -> Array", + ObjectSpace, :undefine_finalizer, [] + end +end