summaryrefslogtreecommitdiff
path: root/spec/ruby/optional
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/optional')
-rw-r--r--spec/ruby/optional/capi/array_spec.rb36
-rw-r--r--spec/ruby/optional/capi/bignum_spec.rb26
-rw-r--r--spec/ruby/optional/capi/binding_spec.rb18
-rw-r--r--spec/ruby/optional/capi/class_spec.rb42
-rw-r--r--spec/ruby/optional/capi/data_spec.rb73
-rw-r--r--spec/ruby/optional/capi/debug_spec.rb14
-rw-r--r--spec/ruby/optional/capi/digest_spec.rb103
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb64
-rw-r--r--spec/ruby/optional/capi/exception_spec.rb54
-rw-r--r--spec/ruby/optional/capi/ext/array_spec.c36
-rw-r--r--spec/ruby/optional/capi/ext/class_spec.c17
-rw-r--r--spec/ruby/optional/capi/ext/constants_spec.c6
-rw-r--r--spec/ruby/optional/capi/ext/data_spec.c4
-rw-r--r--spec/ruby/optional/capi/ext/debug_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/digest_spec.c168
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c28
-rw-r--r--spec/ruby/optional/capi/ext/exception_spec.c27
-rw-r--r--spec/ruby/optional/capi/ext/fiber_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/finalizer_spec.c25
-rw-r--r--spec/ruby/optional/capi/ext/gc_spec.c72
-rw-r--r--spec/ruby/optional/capi/ext/hash_spec.c20
-rw-r--r--spec/ruby/optional/capi/ext/integer_spec.c5
-rw-r--r--spec/ruby/optional/capi/ext/io_spec.c130
-rw-r--r--spec/ruby/optional/capi/ext/kernel_spec.c91
-rw-r--r--spec/ruby/optional/capi/ext/mutex_spec.c23
-rw-r--r--spec/ruby/optional/capi/ext/object_spec.c137
-rw-r--r--spec/ruby/optional/capi/ext/proc_spec.c15
-rw-r--r--spec/ruby/optional/capi/ext/range_spec.c50
-rw-r--r--spec/ruby/optional/capi/ext/rbasic_spec.c64
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h54
-rw-r--r--spec/ruby/optional/capi/ext/set_spec.c65
-rw-r--r--spec/ruby/optional/capi/ext/string_spec.c79
-rw-r--r--spec/ruby/optional/capi/ext/struct_spec.c46
-rw-r--r--spec/ruby/optional/capi/ext/thread_spec.c23
-rw-r--r--spec/ruby/optional/capi/ext/tracepoint_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/typed_data_spec.c14
-rw-r--r--spec/ruby/optional/capi/ext/util_spec.c17
-rw-r--r--spec/ruby/optional/capi/fiber_spec.rb53
-rw-r--r--spec/ruby/optional/capi/file_spec.rb2
-rw-r--r--spec/ruby/optional/capi/finalizer_spec.rb40
-rw-r--r--spec/ruby/optional/capi/fixnum_spec.rb4
-rw-r--r--spec/ruby/optional/capi/fixtures/kernel.rb19
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb35
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb13
-rw-r--r--spec/ruby/optional/capi/hash_spec.rb69
-rw-r--r--spec/ruby/optional/capi/integer_spec.rb19
-rw-r--r--spec/ruby/optional/capi/io_spec.rb324
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb395
-rw-r--r--spec/ruby/optional/capi/module_spec.rb6
-rw-r--r--spec/ruby/optional/capi/mutex_spec.rb13
-rw-r--r--spec/ruby/optional/capi/numeric_spec.rb8
-rw-r--r--spec/ruby/optional/capi/object_spec.rb24
-rw-r--r--spec/ruby/optional/capi/proc_spec.rb53
-rw-r--r--spec/ruby/optional/capi/range_spec.rb144
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb56
-rw-r--r--spec/ruby/optional/capi/regexp_spec.rb4
-rw-r--r--spec/ruby/optional/capi/set_spec.rb96
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb2
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb17
-rw-r--r--spec/ruby/optional/capi/string_spec.rb272
-rw-r--r--spec/ruby/optional/capi/struct_spec.rb105
-rw-r--r--spec/ruby/optional/capi/thread_spec.rb34
-rw-r--r--spec/ruby/optional/capi/time_spec.rb5
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb12
-rw-r--r--spec/ruby/optional/capi/util_spec.rb97
-rw-r--r--spec/ruby/optional/thread_safety/fixtures/classes.rb39
-rw-r--r--spec/ruby/optional/thread_safety/hash_spec.rb210
67 files changed, 3112 insertions, 713 deletions
diff --git a/spec/ruby/optional/capi/array_spec.rb b/spec/ruby/optional/capi/array_spec.rb
index 8e90980c6a..7e87859856 100644
--- a/spec/ruby/optional/capi/array_spec.rb
+++ b/spec/ruby/optional/capi/array_spec.rb
@@ -343,6 +343,42 @@ describe "C-API Array function" do
end
end
+ ruby_version_is ""..."4.0" do
+ describe "rb_iterate" do
+ it "calls an callback function as a block passed to an method" do
+ s = [1,2,3,4]
+ s2 = @s.rb_iterate(s)
+
+ s2.should == s
+
+ # Make sure they're different objects
+ s2.equal?(s).should be_false
+ end
+
+ it "calls a function with the other function available as a block" do
+ h = {a: 1, b: 2}
+
+ @s.rb_iterate_each_pair(h).sort.should == [1,2]
+ end
+
+ it "calls a function which can yield into the original block" do
+ s2 = []
+
+ o = Object.new
+ def o.each
+ yield 1
+ yield 2
+ yield 3
+ yield 4
+ end
+
+ @s.rb_iterate_then_yield(o) { |x| s2 << x }
+
+ s2.should == [1,2,3,4]
+ end
+ end
+ end
+
describe "rb_block_call" do
it "calls an callback function as a block passed to an method" do
s = [1,2,3,4]
diff --git a/spec/ruby/optional/capi/bignum_spec.rb b/spec/ruby/optional/capi/bignum_spec.rb
index cde929af28..179f053eec 100644
--- a/spec/ruby/optional/capi/bignum_spec.rb
+++ b/spec/ruby/optional/capi/bignum_spec.rb
@@ -7,21 +7,23 @@ def ensure_bignum(n)
n
end
-full_range_longs = (fixnum_max == 2**(0.size * 8 - 1) - 1)
+full_range_longs = (fixnum_max == max_long)
+max_ulong = begin
+ require 'rbconfig/sizeof'
+ RbConfig::LIMITS['ULONG_MAX']
+rescue LoadError
+ nil
+end
+# If the system doesn't offer ULONG_MAX, assume 2's complement and derive it
+# from LONG_MAX.
+max_ulong ||= 2 * (max_long + 1) - 1
describe "CApiBignumSpecs" do
before :each do
@s = CApiBignumSpecs.new
-
- if full_range_longs
- @max_long = 2**(0.size * 8 - 1) - 1
- @min_long = -@max_long - 1
- @max_ulong = ensure_bignum(2**(0.size * 8) - 1)
- else
- @max_long = ensure_bignum(2**(0.size * 8 - 1) - 1)
- @min_long = ensure_bignum(-@max_long - 1)
- @max_ulong = ensure_bignum(2**(0.size * 8) - 1)
- end
+ @max_long = max_long
+ @min_long = min_long
+ @max_ulong = ensure_bignum(max_ulong)
end
describe "rb_big2long" do
@@ -123,7 +125,7 @@ describe "CApiBignumSpecs" do
val.should == @max_ulong
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "packs max_ulong into 2 ulongs to allow sign bit" do
val = @s.rb_big_pack_length(@max_ulong)
val.should == 2
diff --git a/spec/ruby/optional/capi/binding_spec.rb b/spec/ruby/optional/capi/binding_spec.rb
index 2165705457..5d0ef2efb8 100644
--- a/spec/ruby/optional/capi/binding_spec.rb
+++ b/spec/ruby/optional/capi/binding_spec.rb
@@ -8,21 +8,9 @@ describe "CApiBindingSpecs" do
end
describe "Kernel#binding" do
- ruby_version_is '3.2' do
- it "raises when called from C" do
- foo = 14
- -> { @b.get_binding }.should raise_error(RuntimeError)
- end
- end
-
- ruby_version_is ''...'3.2' do
- it "gives the top-most Ruby binding when called from C" do
- foo = 14
- b = @b.get_binding
- b.local_variable_get(:foo).should == 14
- b.local_variable_set :foo, 12
- foo.should == 12
- end
+ it "raises when called from C" do
+ foo = 14
+ -> { @b.get_binding }.should raise_error(RuntimeError)
end
end
end
diff --git a/spec/ruby/optional/capi/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb
index 66af381243..7abb5d4ed9 100644
--- a/spec/ruby/optional/capi/class_spec.rb
+++ b/spec/ruby/optional/capi/class_spec.rb
@@ -119,23 +119,21 @@ describe "C-API Class function" do
end
end
- ruby_version_is "3.0" do
- describe "rb_class_new_instance_kw" do
- it "passes arguments and keywords to the #initialize method" do
- obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc)
- obj.args.should == [{pos: 1}]
- obj.kwargs.should == {kw: 2}
+ describe "rb_class_new_instance_kw" do
+ it "passes arguments and keywords to the #initialize method" do
+ obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc)
+ obj.args.should == [{pos: 1}]
+ obj.kwargs.should == {kw: 2}
- obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc)
- obj.args.should == []
- obj.kwargs.should == {}
- end
+ obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc)
+ obj.args.should == []
+ obj.kwargs.should == {}
+ end
- it "raises TypeError if the last argument is not a Hash" do
- -> {
- @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc)
- }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
- end
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc)
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
end
end
@@ -367,6 +365,8 @@ describe "C-API Class function" do
"ClassUnder6",
Class.new)
}.should raise_error(TypeError)
+ ensure
+ CApiClassSpecs.send(:remove_const, :ClassUnder6)
end
it "defines a class for an existing Autoload" do
@@ -489,4 +489,16 @@ describe "C-API Class function" do
@s.rb_class_real(0).should == 0
end
end
+
+ describe "rb_class_get_superclass" do
+ it "returns parent class for a provided class" do
+ a = Class.new
+ @s.rb_class_get_superclass(Class.new(a)).should == a
+ end
+
+ it "returns false when there is no parent class" do
+ @s.rb_class_get_superclass(BasicObject).should == false
+ @s.rb_class_get_superclass(Module.new).should == false
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/data_spec.rb b/spec/ruby/optional/capi/data_spec.rb
index 18c769332e..d000eba6e3 100644
--- a/spec/ruby/optional/capi/data_spec.rb
+++ b/spec/ruby/optional/capi/data_spec.rb
@@ -1,52 +1,53 @@
require_relative 'spec_helper'
+ruby_version_is ""..."3.4" do
+ load_extension("data")
-load_extension("data")
-
-describe "CApiAllocSpecs (a class with an alloc func defined)" do
- it "calls the alloc func" do
- @s = CApiAllocSpecs.new
- @s.wrapped_data.should == 42 # not defined in initialize
- end
-end
-
-describe "CApiWrappedStruct" do
- before :each do
- @s = CApiWrappedStructSpecs.new
- end
-
- it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do
- a = @s.wrap_struct(1024)
- @s.get_struct(a).should == 1024
+ describe "CApiAllocSpecs (a class with an alloc func defined)" do
+ it "calls the alloc func" do
+ @s = CApiAllocSpecs.new
+ @s.wrapped_data.should == 42 # not defined in initialize
+ end
end
- describe "RDATA()" do
- it "returns the struct data" do
- a = @s.wrap_struct(1024)
- @s.get_struct_rdata(a).should == 1024
+ describe "CApiWrappedStruct" do
+ before :each do
+ @s = CApiWrappedStructSpecs.new
end
- it "allows changing the wrapped struct" do
+ it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do
a = @s.wrap_struct(1024)
- @s.change_struct(a, 100)
- @s.get_struct(a).should == 100
+ @s.get_struct(a).should == 1024
end
- it "raises a TypeError if the object does not wrap a struct" do
- -> { @s.get_struct(Object.new) }.should raise_error(TypeError)
+ describe "RDATA()" do
+ it "returns the struct data" do
+ a = @s.wrap_struct(1024)
+ @s.get_struct_rdata(a).should == 1024
+ end
+
+ it "allows changing the wrapped struct" do
+ a = @s.wrap_struct(1024)
+ @s.change_struct(a, 100)
+ @s.get_struct(a).should == 100
+ end
+
+ it "raises a TypeError if the object does not wrap a struct" do
+ -> { @s.get_struct(Object.new) }.should raise_error(TypeError)
+ end
end
- end
- describe "rb_check_type" do
- it "does not raise an exception when checking data objects" do
- a = @s.wrap_struct(1024)
- @s.rb_check_type(a, a).should == true
+ describe "rb_check_type" do
+ it "does not raise an exception when checking data objects" do
+ a = @s.wrap_struct(1024)
+ @s.rb_check_type(a, a).should == true
+ end
end
- end
- describe "DATA_PTR" do
- it "returns the struct data" do
- a = @s.wrap_struct(1024)
- @s.get_struct_data_ptr(a).should == 1024
+ describe "DATA_PTR" do
+ it "returns the struct data" do
+ a = @s.wrap_struct(1024)
+ @s.get_struct_data_ptr(a).should == 1024
+ end
end
end
end
diff --git a/spec/ruby/optional/capi/debug_spec.rb b/spec/ruby/optional/capi/debug_spec.rb
index c8c91417d1..14ba25609c 100644
--- a/spec/ruby/optional/capi/debug_spec.rb
+++ b/spec/ruby/optional/capi/debug_spec.rb
@@ -17,6 +17,7 @@ describe "C-API Debug function" do
describe "rb_debug_inspector_frame_self_get" do
it "returns self" do
@o.rb_debug_inspector_frame_self_get(0).should == @o
+ @o.rb_debug_inspector_frame_self_get(1).should == self
end
end
@@ -35,10 +36,17 @@ describe "C-API Debug function" do
end
it "matches the locations in rb_debug_inspector_backtrace_locations" do
- frames = @o.rb_debug_inspector_open(42);
- frames.each do |_s, _klass, binding, _iseq, backtrace_location|
+ frames = @o.rb_debug_inspector_open(42)
+ frames.each do |_s, klass, binding, iseq, backtrace_location|
if binding
- "#{backtrace_location.path}:#{backtrace_location.lineno}".should == "#{binding.source_location[0]}:#{binding.source_location[1]}"
+ # YJIT modifies Array#each backtraces but leaves its source_location as is
+ unless defined?(RubyVM::YJIT) && klass == Array && iseq.label == "each"
+ binding.source_location.should == [backtrace_location.path, backtrace_location.lineno]
+ end
+ method_name = binding.eval('__method__')
+ if method_name
+ method_name.should == backtrace_location.base_label.to_sym
+ end
end
end
end
diff --git a/spec/ruby/optional/capi/digest_spec.rb b/spec/ruby/optional/capi/digest_spec.rb
new file mode 100644
index 0000000000..65c5ecebb1
--- /dev/null
+++ b/spec/ruby/optional/capi/digest_spec.rb
@@ -0,0 +1,103 @@
+require_relative 'spec_helper'
+
+begin
+ require 'fiddle'
+rescue LoadError
+ return
+end
+
+load_extension('digest')
+
+describe "C-API Digest functions" do
+ before :each do
+ @s = CApiDigestSpecs.new
+ end
+
+ describe "rb_digest_make_metadata" do
+ before :each do
+ @metadata = @s.rb_digest_make_metadata
+ end
+
+ it "should store the block length" do
+ @s.block_length(@metadata).should == 40
+ end
+
+ it "should store the digest length" do
+ @s.digest_length(@metadata).should == 20
+ end
+
+ it "should store the context size" do
+ @s.context_size(@metadata).should == 129
+ end
+ end
+
+ describe "digest plugin" do
+ before :each do
+ @s = CApiDigestSpecs.new
+ @digest = Digest::TestDigest.new
+
+ # A pointer to the CTX type defined in the extension for this spec. Digest does not make the context directly
+ # accessible as part of its API. However, to ensure we are properly loading the plugin, it's useful to have
+ # direct access to the context pointer to verify its contents.
+ @context = Fiddle::Pointer.new(@s.context(@digest))
+ end
+
+ it "should report the block length" do
+ @digest.block_length.should == 40
+ end
+
+ it "should report the digest length" do
+ @digest.digest_length.should == 20
+ end
+
+ it "should initialize the context" do
+ # Our test plugin always writes the string "Initialized\n" when its init function is called.
+ verify_context("Initialized\n")
+ end
+
+ it "should update the digest" do
+ @digest.update("hello world")
+
+ # Our test plugin always writes the string "Updated: <data>\n" when its update function is called.
+ current = "Initialized\nUpdated: hello world"
+ verify_context(current)
+
+ @digest << "blah"
+
+ current = "Initialized\nUpdated: hello worldUpdated: blah"
+ verify_context(current)
+ end
+
+ it "should finalize the digest" do
+ @digest.update("")
+
+ finish_string = @digest.instance_eval { finish }
+
+ # We expect the plugin to write out the last `@digest.digest_length` bytes, followed by the string "Finished\n".
+ #
+ finish_string.should == "d\nUpdated: Finished\n"
+ finish_string.encoding.should == Encoding::ASCII_8BIT
+ end
+
+ it "should reset the context" do
+ @digest.update("foo")
+ verify_context("Initialized\nUpdated: foo")
+
+ @digest.reset
+
+ # The context will be recreated as a result of the `reset` so we must fetch the latest context pointer.
+ @context = Fiddle::Pointer.new(@s.context(@digest))
+
+ verify_context("Initialized\n")
+ end
+
+ def verify_context(current_body)
+ # In the CTX type, the length of the current context contents is stored in the first byte.
+ byte_count = @context[0]
+ byte_count.should == current_body.bytesize
+
+ # After the size byte follows a string.
+ @context[1, byte_count].should == current_body
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb
index aa632b963b..c14983c7ea 100644
--- a/spec/ruby/optional/capi/encoding_spec.rb
+++ b/spec/ruby/optional/capi/encoding_spec.rb
@@ -1,8 +1,9 @@
# -*- encoding: utf-8 -*-
+# frozen_string_literal: false
require_relative 'spec_helper'
require_relative 'fixtures/encoding'
-load_extension('encoding')
+extension_path = load_extension('encoding')
describe :rb_enc_get_index, shared: true do
it "returns the index of the encoding of a String" do
@@ -170,7 +171,7 @@ describe "C-API Encoding function" do
describe "rb_enc_mbc_to_codepoint" do
it "returns the correct codepoint for the given character and size" do
- @s.rb_enc_mbc_to_codepoint("é").should == 0xE9
+ @s.rb_enc_mbc_to_codepoint("é").should == 0xE9
end
it "returns 0 if p == e" do
@@ -559,19 +560,19 @@ describe "C-API Encoding function" do
describe "rb_ascii8bit_encindex" do
it "returns an index for the ASCII-8BIT encoding" do
- @s.rb_ascii8bit_encindex().should >= 0
+ @s.rb_ascii8bit_encindex().should == 0
end
end
describe "rb_utf8_encindex" do
it "returns an index for the UTF-8 encoding" do
- @s.rb_utf8_encindex().should >= 0
+ @s.rb_utf8_encindex().should == 1
end
end
describe "rb_usascii_encindex" do
it "returns an index for the US-ASCII encoding" do
- @s.rb_usascii_encindex().should >= 0
+ @s.rb_usascii_encindex().should == 2
end
end
@@ -657,6 +658,20 @@ describe "C-API Encoding function" do
end
end
+ describe "rb_enc_raise" do
+ it "forces exception message encoding to the specified one" do
+ utf_8_incompatible_string = "\x81".b
+
+ -> {
+ @s.rb_enc_raise(Encoding::UTF_8, RuntimeError, utf_8_incompatible_string)
+ }.should raise_error { |e|
+ e.message.encoding.should == Encoding::UTF_8
+ e.message.valid_encoding?.should == false
+ e.message.bytes.should == utf_8_incompatible_string.bytes
+ }
+ end
+ end
+
describe "rb_uv_to_utf8" do
it 'converts a Unicode codepoint to a UTF-8 C string' do
str = ' ' * 6
@@ -674,6 +689,22 @@ describe "C-API Encoding function" do
end
end
+ describe "rb_enc_left_char_head" do
+ it 'returns the head position of a character' do
+ @s.rb_enc_left_char_head("é", 1).should == 0
+ @s.rb_enc_left_char_head("éééé", 7).should == 6
+
+ @s.rb_enc_left_char_head("a", 0).should == 0
+
+ # unclear if this is intended to work
+ @s.rb_enc_left_char_head("a", 1).should == 1
+
+ # Works because for single-byte encodings rb_enc_left_char_head() just returns the pointer
+ @s.rb_enc_left_char_head("a".force_encoding(Encoding::US_ASCII), 88).should == 88
+ @s.rb_enc_left_char_head("a".b, 88).should == 88
+ end
+ end
+
describe "ONIGENC_MBC_CASE_FOLD" do
it "returns the correct case fold for the given string" do
@s.ONIGENC_MBC_CASE_FOLD("lower").should == ["l", 1]
@@ -691,4 +722,27 @@ describe "C-API Encoding function" do
str.bytes.should == [0, 0x24]
end
end
+
+ describe "rb_define_dummy_encoding" do
+ run = 0
+
+ it "defines the dummy encoding" do
+ @s.rb_define_dummy_encoding("FOO#{run += 1}")
+ enc = Encoding.find("FOO#{run}")
+ enc.should.dummy?
+ end
+
+ it "returns the index of the dummy encoding" do
+ index = @s.rb_define_dummy_encoding("BAR#{run += 1}")
+ index.should == Encoding.list.size - 1
+ end
+
+ it "raises EncodingError if too many encodings" do
+ code = <<-RUBY
+ require #{extension_path.dump}
+ 1_000.times {|i| CApiEncodingSpecs.new.rb_define_dummy_encoding("R_\#{i}") }
+ RUBY
+ ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)')
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/exception_spec.rb b/spec/ruby/optional/capi/exception_spec.rb
index b0a8a2860e..5bc8e26c62 100644
--- a/spec/ruby/optional/capi/exception_spec.rb
+++ b/spec/ruby/optional/capi/exception_spec.rb
@@ -100,6 +100,60 @@ describe "C-API Exception function" do
end
end
+ describe "rb_error_frozen_object" do
+ it "raises a FrozenError regardless of the object's frozen state" do
+ # The type of the argument we supply doesn't matter. The choice here is arbitrary and we only change the type
+ # of the argument to ensure the exception messages are set correctly.
+ -> { @s.rb_error_frozen_object(Array.new) }.should raise_error(FrozenError, "can't modify frozen Array: []")
+ -> { @s.rb_error_frozen_object(Array.new.freeze) }.should raise_error(FrozenError, "can't modify frozen Array: []")
+ end
+
+ it "properly handles recursive rb_error_frozen_object calls" do
+ klass = Class.new(Object)
+ object = klass.new
+ s = @s
+ klass.define_method :inspect do
+ s.rb_error_frozen_object(object)
+ end
+
+ -> { @s.rb_error_frozen_object(object) }.should raise_error(FrozenError, "can't modify frozen #{klass}: ...")
+ end
+ end
+
+ describe "rb_syserr_new" do
+ it "returns system error with default message when passed message is NULL" do
+ exception = @s.rb_syserr_new(Errno::ENOENT::Errno, nil)
+ exception.class.should == Errno::ENOENT
+ exception.message.should include("No such file or directory")
+ exception.should.is_a?(SystemCallError)
+ end
+
+ it "returns system error with custom message" do
+ exception = @s.rb_syserr_new(Errno::ENOENT::Errno, "custom message")
+
+ exception.message.should include("custom message")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+ end
+
+ describe "rb_syserr_new_str" do
+ it "returns system error with default message when passed message is nil" do
+ exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, nil)
+
+ exception.message.should include("No such file or directory")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+
+ it "returns system error with custom message" do
+ exception = @s.rb_syserr_new_str(Errno::ENOENT::Errno, "custom message")
+ exception.message.should include("custom message")
+ exception.class.should == Errno::ENOENT
+ exception.should.is_a?(SystemCallError)
+ end
+ end
+
describe "rb_make_exception" do
it "returns a RuntimeError when given a String argument" do
e = @s.rb_make_exception(["Message"])
diff --git a/spec/ruby/optional/capi/ext/array_spec.c b/spec/ruby/optional/capi/ext/array_spec.c
index 9386239813..628c4df9d7 100644
--- a/spec/ruby/optional/capi/ext/array_spec.c
+++ b/spec/ruby/optional/capi/ext/array_spec.c
@@ -196,6 +196,16 @@ static VALUE copy_ary(RB_BLOCK_CALL_FUNC_ARGLIST(el, new_ary)) {
return rb_ary_push(new_ary, el);
}
+#ifndef RUBY_VERSION_IS_4_0
+static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(rb_each, ary, copy_ary, new_ary);
+
+ return new_ary;
+}
+#endif
+
static VALUE array_spec_rb_block_call(VALUE self, VALUE ary) {
VALUE new_ary = rb_ary_new();
@@ -208,6 +218,20 @@ static VALUE sub_pair(RB_BLOCK_CALL_FUNC_ARGLIST(el, holder)) {
return rb_ary_push(holder, rb_ary_entry(el, 1));
}
+#ifndef RUBY_VERSION_IS_4_0
+static VALUE each_pair(VALUE obj) {
+ return rb_funcall(obj, rb_intern("each_pair"), 0);
+}
+
+static VALUE array_spec_rb_iterate_each_pair(VALUE self, VALUE obj) {
+ VALUE new_ary = rb_ary_new();
+
+ rb_iterate(each_pair, obj, sub_pair, new_ary);
+
+ return new_ary;
+}
+#endif
+
static VALUE array_spec_rb_block_call_each_pair(VALUE self, VALUE obj) {
VALUE new_ary = rb_ary_new();
@@ -221,6 +245,13 @@ static VALUE iter_yield(RB_BLOCK_CALL_FUNC_ARGLIST(el, ary)) {
return Qnil;
}
+#ifndef RUBY_VERSION_IS_4_0
+static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) {
+ rb_iterate(rb_each, obj, iter_yield, obj);
+ return Qnil;
+}
+#endif
+
static VALUE array_spec_rb_block_call_then_yield(VALUE self, VALUE obj) {
rb_block_call(obj, rb_intern("each"), 0, 0, iter_yield, obj);
return Qnil;
@@ -283,6 +314,11 @@ void Init_array_spec(void) {
rb_define_method(cls, "rb_ary_plus", array_spec_rb_ary_plus, 2);
rb_define_method(cls, "rb_ary_unshift", array_spec_rb_ary_unshift, 2);
rb_define_method(cls, "rb_assoc_new", array_spec_rb_assoc_new, 2);
+#ifndef RUBY_VERSION_IS_4_0
+ rb_define_method(cls, "rb_iterate", array_spec_rb_iterate, 1);
+ rb_define_method(cls, "rb_iterate_each_pair", array_spec_rb_iterate_each_pair, 1);
+ rb_define_method(cls, "rb_iterate_then_yield", array_spec_rb_iterate_then_yield, 1);
+#endif
rb_define_method(cls, "rb_block_call", array_spec_rb_block_call, 1);
rb_define_method(cls, "rb_block_call_each_pair", array_spec_rb_block_call_each_pair, 1);
rb_define_method(cls, "rb_block_call_then_yield", array_spec_rb_block_call_then_yield, 1);
diff --git a/spec/ruby/optional/capi/ext/class_spec.c b/spec/ruby/optional/capi/ext/class_spec.c
index 589025f677..8ac0e7a93f 100644
--- a/spec/ruby/optional/capi/ext/class_spec.c
+++ b/spec/ruby/optional/capi/ext/class_spec.c
@@ -65,20 +65,22 @@ static VALUE class_spec_rb_class_new_instance(VALUE self, VALUE args, VALUE klas
return rb_class_new_instance(RARRAY_LENINT(args), RARRAY_PTR(args), klass);
}
-#ifdef RUBY_VERSION_IS_3_0
static VALUE class_spec_rb_class_new_instance_kw(VALUE self, VALUE args, VALUE klass) {
return rb_class_new_instance_kw(RARRAY_LENINT(args), RARRAY_PTR(args), klass, RB_PASS_KEYWORDS);
}
-#endif
static VALUE class_spec_rb_class_real(VALUE self, VALUE object) {
- if(rb_type_p(object, T_FIXNUM)) {
+ if (rb_type_p(object, T_FIXNUM)) {
return INT2FIX(rb_class_real(FIX2INT(object)));
} else {
return rb_class_real(CLASS_OF(object));
}
}
+static VALUE class_spec_rb_class_get_superclass(VALUE self, VALUE klass) {
+ return rb_class_get_superclass(klass);
+}
+
static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) {
return rb_class_superclass(klass);
}
@@ -116,19 +118,19 @@ VALUE class_spec_define_attr(VALUE self, VALUE klass, VALUE sym, VALUE read, VAL
}
static VALUE class_spec_rb_define_class(VALUE self, VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class(RSTRING_PTR(name), super);
}
static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer,
VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class_under(outer, RSTRING_PTR(name), super);
}
static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer,
VALUE name, VALUE super) {
- if(NIL_P(super)) super = 0;
+ if (NIL_P(super)) super = 0;
return rb_define_class_id_under(outer, SYM2ID(name), super);
}
@@ -156,10 +158,9 @@ void Init_class_spec(void) {
rb_define_method(cls, "rb_class_private_instance_methods", class_spec_rb_class_private_instance_methods, -1);
rb_define_method(cls, "rb_class_new", class_spec_rb_class_new, 1);
rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 2);
-#ifdef RUBY_VERSION_IS_3_0
rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2);
-#endif
rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1);
+ rb_define_method(cls, "rb_class_get_superclass", class_spec_rb_class_get_superclass, 1);
rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1);
rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2);
rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2);
diff --git a/spec/ruby/optional/capi/ext/constants_spec.c b/spec/ruby/optional/capi/ext/constants_spec.c
index 9aee8db37f..05819ea476 100644
--- a/spec/ruby/optional/capi/ext/constants_spec.c
+++ b/spec/ruby/optional/capi/ext/constants_spec.c
@@ -14,9 +14,6 @@ defconstfunc(rb_cBinding)
defconstfunc(rb_cClass)
defconstfunc(rb_cComplex)
defconstfunc(rb_mComparable)
-#ifndef RUBY_VERSION_IS_3_0
-defconstfunc(rb_cData)
-#endif
defconstfunc(rb_cDir)
defconstfunc(rb_cEncoding)
defconstfunc(rb_mEnumerable)
@@ -97,9 +94,6 @@ void Init_constants_spec(void) {
rb_define_method(cls, "rb_cClass", constants_spec_rb_cClass, 0);
rb_define_method(cls, "rb_cComplex", constants_spec_rb_cComplex, 0);
rb_define_method(cls, "rb_mComparable", constants_spec_rb_mComparable, 0);
- #ifndef RUBY_VERSION_IS_3_0
- rb_define_method(cls, "rb_cData", constants_spec_rb_cData, 0);
- #endif
rb_define_method(cls, "rb_cDir", constants_spec_rb_cDir, 0);
rb_define_method(cls, "rb_cEncoding", constants_spec_rb_cEncoding, 0);
rb_define_method(cls, "rb_mEnumerable", constants_spec_rb_mEnumerable, 0);
diff --git a/spec/ruby/optional/capi/ext/data_spec.c b/spec/ruby/optional/capi/ext/data_spec.c
index ef069ef0ba..efefe37c3a 100644
--- a/spec/ruby/optional/capi/ext/data_spec.c
+++ b/spec/ruby/optional/capi/ext/data_spec.c
@@ -3,6 +3,7 @@
#include <string.h>
+#ifndef RUBY_VERSION_IS_3_4
#ifdef __cplusplus
extern "C" {
#endif
@@ -70,8 +71,10 @@ VALUE sws_rb_check_type(VALUE self, VALUE obj, VALUE other) {
rb_check_type(obj, TYPE(other));
return Qtrue;
}
+#endif
void Init_data_spec(void) {
+#ifndef RUBY_VERSION_IS_3_4
VALUE cls = rb_define_class("CApiAllocSpecs", rb_cObject);
rb_define_alloc_func(cls, sdaf_alloc_func);
rb_define_method(cls, "wrapped_data", sdaf_get_struct, 0);
@@ -82,6 +85,7 @@ void Init_data_spec(void) {
rb_define_method(cls, "get_struct_data_ptr", sws_get_struct_data_ptr, 1);
rb_define_method(cls, "change_struct", sws_change_struct, 2);
rb_define_method(cls, "rb_check_type", sws_rb_check_type, 2);
+#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/debug_spec.c b/spec/ruby/optional/capi/ext/debug_spec.c
index 344dfc33fa..9131eda78b 100644
--- a/spec/ruby/optional/capi/ext/debug_spec.c
+++ b/spec/ruby/optional/capi/ext/debug_spec.c
@@ -45,7 +45,7 @@ static VALUE rb_debug_inspector_frame_iseq_get_callback(const rb_debug_inspector
return rb_debug_inspector_frame_iseq_get(dc, NUM2LONG((VALUE) ptr));
}
-static VALUE debug_spec_callback_data(VALUE self){
+static VALUE debug_spec_callback_data(VALUE self) {
return callback_data;
}
diff --git a/spec/ruby/optional/capi/ext/digest_spec.c b/spec/ruby/optional/capi/ext/digest_spec.c
new file mode 100644
index 0000000000..65c8defa20
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/digest_spec.c
@@ -0,0 +1,168 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#include "ruby/digest.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DIGEST_LENGTH 20
+#define BLOCK_LENGTH 40
+
+const char *init_string = "Initialized\n";
+const char *update_string = "Updated: ";
+const char *finish_string = "Finished\n";
+
+#define PAYLOAD_SIZE 128
+
+typedef struct CTX {
+ uint8_t pos;
+ char payload[PAYLOAD_SIZE];
+} CTX;
+
+void* context = NULL;
+
+int digest_spec_plugin_init(void *raw_ctx) {
+ // Make the context accessible to tests. This isn't safe, but there's no way to access the context otherwise.
+ context = raw_ctx;
+
+ struct CTX *ctx = (struct CTX *)raw_ctx;
+ size_t len = strlen(init_string);
+
+ // Clear the payload since this init function will be invoked as part of the `reset` operation.
+ memset(ctx->payload, 0, PAYLOAD_SIZE);
+
+ // Write a simple value we can verify in tests.
+ // This is not what a real digest would do, but we're using a dummy digest plugin to test interactions.
+ memcpy(ctx->payload, init_string, len);
+ ctx->pos = (uint8_t) len;
+
+ return 1;
+}
+
+void digest_spec_plugin_update(void *raw_ctx, unsigned char *ptr, size_t size) {
+ struct CTX *ctx = (struct CTX *)raw_ctx;
+ size_t update_str_len = strlen(update_string);
+
+ if (ctx->pos + update_str_len + size >= PAYLOAD_SIZE) {
+ rb_raise(rb_eRuntimeError, "update size too large; reset the digest and write fewer updates");
+ }
+
+ // Write the supplied value to the payload so it can be easily verified in test.
+ // This is not what a real digest would do, but we're using a dummy digest plugin to test interactions.
+ memcpy(ctx->payload + ctx->pos, update_string, update_str_len);
+ ctx->pos += update_str_len;
+
+ memcpy(ctx->payload + ctx->pos, ptr, size);
+ ctx->pos += size;
+
+ return;
+}
+
+int digest_spec_plugin_finish(void *raw_ctx, unsigned char *ptr) {
+ struct CTX *ctx = (struct CTX *)raw_ctx;
+ size_t finish_string_len = strlen(finish_string);
+
+ // We're always going to write DIGEST_LENGTH bytes. In a real plugin, this would be the digest value. Here we
+ // write out a text string in order to make validation in tests easier.
+ //
+ // In order to delineate the output more clearly from an `Digest#update` call, we always write out the
+ // `finish_string` message. That leaves `DIGEST_LENGTH - finish_string_len` bytes to read out of the context.
+ size_t context_bytes = DIGEST_LENGTH - finish_string_len;
+
+ memcpy(ptr, ctx->payload + (ctx->pos - context_bytes), context_bytes);
+ memcpy(ptr + context_bytes, finish_string, finish_string_len);
+
+ return 1;
+}
+
+static const rb_digest_metadata_t metadata = {
+ // The RUBY_DIGEST_API_VERSION value comes from ruby/digest.h and may vary based on the Ruby being tested. Since
+ // it isn't publicly exposed in the digest gem, we ignore for these tests. Either the test hard-codes an expected
+ // value and is subject to breaking depending on the Ruby being run or we publicly expose `RUBY_DIGEST_API_VERSION`,
+ // in which case the test would pass trivially.
+ RUBY_DIGEST_API_VERSION,
+ DIGEST_LENGTH,
+ BLOCK_LENGTH,
+ sizeof(CTX),
+ (rb_digest_hash_init_func_t) digest_spec_plugin_init,
+ (rb_digest_hash_update_func_t) digest_spec_plugin_update,
+ (rb_digest_hash_finish_func_t) digest_spec_plugin_finish,
+};
+
+// The `get_metadata_ptr` function is not publicly available in the digest gem. However, we need to use
+// to extract the `rb_digest_metadata_t*` value set up by the plugin so we reproduce and adjust the
+// definition here.
+//
+// Taken and adapted from https://github.com/ruby/digest/blob/v3.2.0/ext/digest/digest.c#L558-L568
+static rb_digest_metadata_t * get_metadata_ptr(VALUE obj) {
+ rb_digest_metadata_t *algo;
+
+#ifdef DIGEST_USE_RB_EXT_RESOLVE_SYMBOL
+ // In the digest gem there is an additional data type check performed before reading the value out.
+ // Since the type definition isn't public, we can't use it as part of a type check here so we omit it.
+ // This is safe to do because this code is intended to only load digest plugins written as part of this test suite.
+ algo = (rb_digest_metadata_t *) RTYPEDDATA_DATA(obj);
+#else
+# undef RUBY_UNTYPED_DATA_WARNING
+# define RUBY_UNTYPED_DATA_WARNING 0
+ Data_Get_Struct(obj, rb_digest_metadata_t, algo);
+#endif
+
+ return algo;
+}
+
+VALUE digest_spec_rb_digest_make_metadata(VALUE self) {
+ return rb_digest_make_metadata(&metadata);
+}
+
+VALUE digest_spec_block_length(VALUE self, VALUE meta) {
+ rb_digest_metadata_t* algo = get_metadata_ptr(meta);
+
+ return SIZET2NUM(algo->block_len);
+}
+
+VALUE digest_spec_digest_length(VALUE self, VALUE meta) {
+ rb_digest_metadata_t* algo = get_metadata_ptr(meta);
+
+ return SIZET2NUM(algo->digest_len);
+}
+
+VALUE digest_spec_context_size(VALUE self, VALUE meta) {
+ rb_digest_metadata_t* algo = get_metadata_ptr(meta);
+
+ return SIZET2NUM(algo->ctx_size);
+}
+
+#ifndef PTR2NUM
+#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x)))
+#endif
+
+VALUE digest_spec_context(VALUE self, VALUE digest) {
+ return PTR2NUM(context);
+}
+
+void Init_digest_spec(void) {
+ VALUE cls;
+
+ cls = rb_define_class("CApiDigestSpecs", rb_cObject);
+ rb_define_method(cls, "rb_digest_make_metadata", digest_spec_rb_digest_make_metadata, 0);
+ rb_define_method(cls, "block_length", digest_spec_block_length, 1);
+ rb_define_method(cls, "digest_length", digest_spec_digest_length, 1);
+ rb_define_method(cls, "context_size", digest_spec_context_size, 1);
+ rb_define_method(cls, "context", digest_spec_context, 1);
+
+ VALUE mDigest, cDigest_Base, cDigest;
+
+ mDigest = rb_define_module("Digest");
+ mDigest = rb_digest_namespace();
+ cDigest_Base = rb_const_get(mDigest, rb_intern_const("Base"));
+
+ cDigest = rb_define_class_under(mDigest, "TestDigest", cDigest_Base);
+ rb_iv_set(cDigest, "metadata", rb_digest_make_metadata(&metadata));
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c
index a0136530f2..aa8662cfbd 100644
--- a/spec/ruby/optional/capi/ext/encoding_spec.c
+++ b/spec/ruby/optional/capi/ext/encoding_spec.c
@@ -12,7 +12,7 @@ static VALUE encoding_spec_MBCLEN_CHARFOUND_P(VALUE self, VALUE obj) {
}
static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) {
- if(ENC_CODERANGE_ASCIIONLY(obj)) {
+ if (ENC_CODERANGE_ASCIIONLY(obj)) {
return Qtrue;
} else {
return Qfalse;
@@ -61,13 +61,13 @@ static VALUE encoding_spec_rb_filesystem_encindex(VALUE self) {
static VALUE encoding_spec_rb_default_internal_encoding(VALUE self) {
rb_encoding* enc = rb_default_internal_encoding();
- if(enc == 0) return Qnil;
+ if (enc == 0) return Qnil;
return rb_str_new2(enc->name);
}
static VALUE encoding_spec_rb_default_external_encoding(VALUE self) {
rb_encoding* enc = rb_default_external_encoding();
- if(enc == 0) return Qnil;
+ if (enc == 0) return Qnil;
return rb_str_new2(enc->name);
}
@@ -86,7 +86,7 @@ static VALUE encoding_spec_rb_enc_associate_index(VALUE self, VALUE obj, VALUE i
static VALUE encoding_spec_rb_enc_compatible(VALUE self, VALUE a, VALUE b) {
rb_encoding* enc = rb_enc_compatible(a, b);
- if(!enc) return INT2FIX(0);
+ if (!enc) return INT2FIX(0);
return rb_enc_from_encoding(enc);
}
@@ -271,6 +271,13 @@ static VALUE encoding_spec_rb_enc_str_asciionly_p(VALUE self, VALUE str) {
}
}
+static VALUE encoding_spec_rb_enc_raise(VALUE self, VALUE encoding, VALUE exception_class, VALUE format) {
+ rb_encoding *e = rb_to_encoding(encoding);
+ const char *f = RSTRING_PTR(format);
+
+ rb_enc_raise(e, exception_class, "%s", f);
+}
+
static VALUE encoding_spec_rb_uv_to_utf8(VALUE self, VALUE buf, VALUE num) {
int len = rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num));
RB_ENC_CODERANGE_CLEAR(buf);
@@ -307,6 +314,16 @@ static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VA
return LONG2FIX(rb_enc_strlen(p, e, rb_to_encoding(encoding)));
}
+static VALUE encoding_spec_rb_enc_left_char_head(VALUE self, VALUE str, VALUE offset) {
+ char *ptr = RSTRING_PTR(str);
+ char *result = rb_enc_left_char_head(ptr, ptr + NUM2INT(offset), RSTRING_END(str), rb_enc_get(str));
+ return LONG2NUM(result - ptr);
+}
+
+static VALUE encoding_spec_rb_define_dummy_encoding(VALUE self, VALUE name) {
+ return INT2NUM(rb_define_dummy_encoding(RSTRING_PTR(name)));
+}
+
void Init_encoding_spec(void) {
VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@@ -362,8 +379,11 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2);
rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1);
rb_define_method(cls, "rb_enc_str_asciionly_p", encoding_spec_rb_enc_str_asciionly_p, 1);
+ rb_define_method(cls, "rb_enc_raise", encoding_spec_rb_enc_raise, 3);
rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2);
rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1);
+ rb_define_method(cls, "rb_enc_left_char_head", encoding_spec_rb_enc_left_char_head, 2);
+ rb_define_method(cls, "rb_define_dummy_encoding", encoding_spec_rb_define_dummy_encoding, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/exception_spec.c b/spec/ruby/optional/capi/ext/exception_spec.c
index e1114aabb8..c3b94d7bcd 100644
--- a/spec/ruby/optional/capi/ext/exception_spec.c
+++ b/spec/ruby/optional/capi/ext/exception_spec.c
@@ -27,7 +27,7 @@ VALUE exception_spec_rb_exc_new3(VALUE self, VALUE str) {
}
VALUE exception_spec_rb_exc_raise(VALUE self, VALUE exc) {
- if (self != Qundef) rb_exc_raise(exc);
+ if (self != Qundef) rb_exc_raise(exc);
return Qnil;
}
@@ -36,6 +36,28 @@ VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) {
return Qnil;
}
+NORETURN(VALUE exception_spec_rb_error_frozen_object(VALUE self, VALUE object));
+
+VALUE exception_spec_rb_error_frozen_object(VALUE self, VALUE object) {
+ rb_error_frozen_object(object);
+ UNREACHABLE_RETURN(Qnil);
+}
+
+VALUE exception_spec_rb_syserr_new(VALUE self, VALUE num, VALUE msg) {
+ int n = NUM2INT(num);
+ char *cstr = NULL;
+
+ if (msg != Qnil) {
+ cstr = StringValuePtr(msg);
+ }
+
+ return rb_syserr_new(n, cstr);
+}
+
+VALUE exception_spec_rb_syserr_new_str(VALUE self, VALUE num, VALUE msg) {
+ int n = NUM2INT(num);
+ return rb_syserr_new_str(n, msg);
+}
VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) {
int argc = RARRAY_LENINT(ary);
@@ -51,6 +73,9 @@ void Init_exception_spec(void) {
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1);
rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1);
+ rb_define_method(cls, "rb_error_frozen_object", exception_spec_rb_error_frozen_object, 1);
+ rb_define_method(cls, "rb_syserr_new", exception_spec_rb_syserr_new, 2);
+ rb_define_method(cls, "rb_syserr_new_str", exception_spec_rb_syserr_new_str, 2);
rb_define_method(cls, "rb_make_exception", exception_spec_rb_make_exception, 1);
}
diff --git a/spec/ruby/optional/capi/ext/fiber_spec.c b/spec/ruby/optional/capi/ext/fiber_spec.c
index f06a54494e..db54f7ad8c 100644
--- a/spec/ruby/optional/capi/ext/fiber_spec.c
+++ b/spec/ruby/optional/capi/ext/fiber_spec.c
@@ -44,12 +44,10 @@ VALUE fiber_spec_rb_fiber_new(VALUE self) {
return rb_fiber_new(fiber_spec_rb_fiber_new_function, Qnil);
}
-#ifdef RUBY_VERSION_IS_3_1
VALUE fiber_spec_rb_fiber_raise(int argc, VALUE *argv, VALUE self) {
VALUE fiber = argv[0];
return rb_fiber_raise(fiber, argc-1, argv+1);
}
-#endif
void Init_fiber_spec(void) {
VALUE cls = rb_define_class("CApiFiberSpecs", rb_cObject);
@@ -58,10 +56,7 @@ void Init_fiber_spec(void) {
rb_define_method(cls, "rb_fiber_resume", fiber_spec_rb_fiber_resume, 2);
rb_define_method(cls, "rb_fiber_yield", fiber_spec_rb_fiber_yield, 1);
rb_define_method(cls, "rb_fiber_new", fiber_spec_rb_fiber_new, 0);
-
-#ifdef RUBY_VERSION_IS_3_1
rb_define_method(cls, "rb_fiber_raise", fiber_spec_rb_fiber_raise, -1);
-#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/finalizer_spec.c b/spec/ruby/optional/capi/ext/finalizer_spec.c
new file mode 100644
index 0000000000..83347da912
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/finalizer_spec.c
@@ -0,0 +1,25 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static VALUE define_finalizer(VALUE self, VALUE obj, VALUE finalizer) {
+ return rb_define_finalizer(obj, finalizer);
+}
+
+static VALUE undefine_finalizer(VALUE self, VALUE obj) {
+ return rb_undefine_finalizer(obj);
+}
+
+void Init_finalizer_spec(void) {
+ VALUE cls = rb_define_class("CApiFinalizerSpecs", rb_cObject);
+
+ rb_define_method(cls, "rb_define_finalizer", define_finalizer, 2);
+ rb_define_method(cls, "rb_undefine_finalizer", undefine_finalizer, 1);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/spec/ruby/optional/capi/ext/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c
index 082e4af59c..2637ad27ac 100644
--- a/spec/ruby/optional/capi/ext/gc_spec.c
+++ b/spec/ruby/optional/capi/ext/gc_spec.c
@@ -8,9 +8,16 @@ extern "C" {
VALUE registered_tagged_value;
VALUE registered_reference_value;
VALUE registered_before_rb_gc_register_address;
-VALUE registered_before_rb_global_variable;
+VALUE registered_before_rb_global_variable_string;
+VALUE registered_before_rb_global_variable_bignum;
+VALUE registered_before_rb_global_variable_float;
+VALUE registered_after_rb_global_variable_string;
+VALUE registered_after_rb_global_variable_bignum;
+VALUE registered_after_rb_global_variable_float;
VALUE rb_gc_register_address_outside_init;
+VALUE rb_gc_register_mark_object_not_referenced_float;
+
static VALUE registered_tagged_address(VALUE self) {
return registered_tagged_value;
}
@@ -23,13 +30,33 @@ static VALUE get_registered_before_rb_gc_register_address(VALUE self) {
return registered_before_rb_gc_register_address;
}
-static VALUE get_registered_before_rb_global_variable(VALUE self) {
- return registered_before_rb_global_variable;
+static VALUE get_registered_before_rb_global_variable_string(VALUE self) {
+ return registered_before_rb_global_variable_string;
+}
+
+static VALUE get_registered_before_rb_global_variable_bignum(VALUE self) {
+ return registered_before_rb_global_variable_bignum;
+}
+
+static VALUE get_registered_before_rb_global_variable_float(VALUE self) {
+ return registered_before_rb_global_variable_float;
+}
+
+static VALUE get_registered_after_rb_global_variable_string(VALUE self) {
+ return registered_after_rb_global_variable_string;
+}
+
+static VALUE get_registered_after_rb_global_variable_bignum(VALUE self) {
+ return registered_after_rb_global_variable_bignum;
+}
+
+static VALUE get_registered_after_rb_global_variable_float(VALUE self) {
+ return registered_after_rb_global_variable_float;
}
static VALUE gc_spec_rb_gc_register_address(VALUE self) {
- rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_");
rb_gc_register_address(&rb_gc_register_address_outside_init);
+ rb_gc_register_address_outside_init = rb_str_new_cstr("rb_gc_register_address() outside Init_");
return rb_gc_register_address_outside_init;
}
@@ -51,7 +78,7 @@ static VALUE gc_spec_rb_gc(VALUE self) {
return Qnil;
}
-static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key){
+static VALUE gc_spec_rb_gc_latest_gc_info(VALUE self, VALUE hash_or_key) {
return rb_gc_latest_gc_info(hash_or_key);
}
@@ -65,23 +92,47 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) {
return Qnil;
}
+static VALUE gc_spec_rb_gc_register_mark_object_not_referenced_float(VALUE self) {
+ return rb_gc_register_mark_object_not_referenced_float;
+}
+
void Init_gc_spec(void) {
VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject);
- registered_tagged_value = INT2NUM(10);
- registered_reference_value = rb_str_new2("Globally registered data");
rb_gc_register_address(&registered_tagged_value);
rb_gc_register_address(&registered_reference_value);
rb_gc_register_address(&registered_before_rb_gc_register_address);
- rb_global_variable(&registered_before_rb_global_variable);
+ rb_global_variable(&registered_before_rb_global_variable_string);
+ rb_global_variable(&registered_before_rb_global_variable_bignum);
+ rb_global_variable(&registered_before_rb_global_variable_float);
+ registered_tagged_value = INT2NUM(10);
+ registered_reference_value = rb_str_new2("Globally registered data");
registered_before_rb_gc_register_address = rb_str_new_cstr("registered before rb_gc_register_address()");
- registered_before_rb_global_variable = rb_str_new_cstr("registered before rb_global_variable()");
+
+ registered_before_rb_global_variable_string = rb_str_new_cstr("registered before rb_global_variable()");
+ registered_before_rb_global_variable_bignum = LL2NUM(INT64_MAX);
+ registered_before_rb_global_variable_float = DBL2NUM(3.14);
+
+ registered_after_rb_global_variable_string = rb_str_new_cstr("registered after rb_global_variable()");
+ rb_global_variable(&registered_after_rb_global_variable_string);
+ registered_after_rb_global_variable_bignum = LL2NUM(INT64_MAX);
+ rb_global_variable(&registered_after_rb_global_variable_bignum);
+ registered_after_rb_global_variable_float = DBL2NUM(6.28);
+ rb_global_variable(&registered_after_rb_global_variable_float);
+
+ rb_gc_register_mark_object_not_referenced_float = DBL2NUM(1.61);
+ rb_gc_register_mark_object(rb_gc_register_mark_object_not_referenced_float);
rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0);
rb_define_method(cls, "registered_reference_address", registered_reference_address, 0);
rb_define_method(cls, "registered_before_rb_gc_register_address", get_registered_before_rb_gc_register_address, 0);
- rb_define_method(cls, "registered_before_rb_global_variable", get_registered_before_rb_global_variable, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_string", get_registered_before_rb_global_variable_string, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_bignum", get_registered_before_rb_global_variable_bignum, 0);
+ rb_define_method(cls, "registered_before_rb_global_variable_float", get_registered_before_rb_global_variable_float, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_string", get_registered_after_rb_global_variable_string, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_bignum", get_registered_after_rb_global_variable_bignum, 0);
+ rb_define_method(cls, "registered_after_rb_global_variable_float", get_registered_after_rb_global_variable_float, 0);
rb_define_method(cls, "rb_gc_register_address", gc_spec_rb_gc_register_address, 0);
rb_define_method(cls, "rb_gc_unregister_address", gc_spec_rb_gc_unregister_address, 0);
rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0);
@@ -89,6 +140,7 @@ void Init_gc_spec(void) {
rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0);
rb_define_method(cls, "rb_gc_adjust_memory_usage", gc_spec_rb_gc_adjust_memory_usage, 1);
rb_define_method(cls, "rb_gc_register_mark_object", gc_spec_rb_gc_register_mark_object, 1);
+ rb_define_method(cls, "rb_gc_register_mark_object_not_referenced_float", gc_spec_rb_gc_register_mark_object_not_referenced_float, 0);
rb_define_method(cls, "rb_gc_latest_gc_info", gc_spec_rb_gc_latest_gc_info, 1);
}
diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c
index 7f38708915..653917f2c4 100644
--- a/spec/ruby/optional/capi/ext/hash_spec.c
+++ b/spec/ruby/optional/capi/ext/hash_spec.c
@@ -105,6 +105,10 @@ VALUE hash_spec_rb_hash_new(VALUE self) {
return rb_hash_new();
}
+VALUE hash_spec_rb_hash_new_capa(VALUE self, VALUE capacity) {
+ return rb_hash_new_capa(NUM2LONG(capacity));
+}
+
VALUE rb_ident_hash_new(void); /* internal.h, used in ripper */
VALUE hash_spec_rb_ident_hash_new(VALUE self) {
@@ -128,6 +132,20 @@ VALUE hash_spec_compute_a_hash_code(VALUE self, VALUE seed) {
return ULONG2NUM(h);
}
+VALUE hash_spec_rb_hash_bulk_insert(VALUE self, VALUE array_len, VALUE array, VALUE hash) {
+ VALUE* ptr;
+
+ if (array == Qnil) {
+ ptr = NULL;
+ } else {
+ ptr = RARRAY_PTR(array);
+ }
+
+ long len = FIX2LONG(array_len);
+ rb_hash_bulk_insert(len, ptr, hash);
+ return Qnil;
+}
+
void Init_hash_spec(void) {
VALUE cls = rb_define_class("CApiHashSpecs", rb_cObject);
rb_define_method(cls, "rb_hash", hash_spec_rb_hash, 1);
@@ -149,10 +167,12 @@ void Init_hash_spec(void) {
rb_define_method(cls, "rb_hash_lookup2", hash_spec_rb_hash_lookup2, 3);
rb_define_method(cls, "rb_hash_lookup2_default_undef", hash_spec_rb_hash_lookup2_default_undef, 2);
rb_define_method(cls, "rb_hash_new", hash_spec_rb_hash_new, 0);
+ rb_define_method(cls, "rb_hash_new_capa", hash_spec_rb_hash_new_capa, 1);
rb_define_method(cls, "rb_ident_hash_new", hash_spec_rb_ident_hash_new, 0);
rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1);
rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2);
rb_define_method(cls, "compute_a_hash_code", hash_spec_compute_a_hash_code, 1);
+ rb_define_method(cls, "rb_hash_bulk_insert", hash_spec_rb_hash_bulk_insert, 3);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/integer_spec.c b/spec/ruby/optional/capi/ext/integer_spec.c
index 16cd95f111..792fc0652a 100644
--- a/spec/ruby/optional/capi/ext/integer_spec.c
+++ b/spec/ruby/optional/capi/ext/integer_spec.c
@@ -6,8 +6,7 @@ extern "C" {
#endif
static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value,
- VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags)
-{
+ VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) {
int result = rb_integer_pack(value, (void*)RSTRING_PTR(words), FIX2INT(numwords),
FIX2INT(wordsize), FIX2INT(nails), FIX2INT(flags));
return INT2FIX(result);
@@ -15,7 +14,7 @@ static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value,
RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y); /* internal.h, used in ripper */
-static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b){
+static VALUE integer_spec_rb_int_positive_pow(VALUE self, VALUE a, VALUE b) {
return rb_int_positive_pow(FIX2INT(a), FIX2INT(b));
}
diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c
index f257cef554..f3ede15729 100644
--- a/spec/ruby/optional/capi/ext/io_spec.c
+++ b/spec/ruby/optional/capi/ext/io_spec.c
@@ -28,9 +28,7 @@ static int set_non_blocking(int fd) {
}
static int io_spec_get_fd(VALUE io) {
- rb_io_t* fp;
- GetOpenFile(io, fp);
- return fp->fd;
+ return rb_io_descriptor(io);
}
VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) {
@@ -130,7 +128,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
rb_sys_fail("set_non_blocking failed");
#ifndef SET_NON_BLOCKING_FAILS_ALWAYS
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) {
return Qnil;
}
@@ -139,9 +137,9 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
errno = saved_errno;
}
- ret = rb_io_wait_readable(fd);
+ ret = rb_io_maybe_wait_readable(errno, io, Qnil);
- if(RTEST(read_p)) {
+ if (RTEST(read_p)) {
ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF);
if (r != RB_IO_WAIT_READABLE_BUF) {
perror("read");
@@ -153,28 +151,80 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) {
return ret ? Qtrue : Qfalse;
#else
- UNREACHABLE;
+ UNREACHABLE_RETURN(Qnil);
#endif
}
VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) {
- int ret = rb_io_wait_writable(io_spec_get_fd(io));
+ int ret = rb_io_maybe_wait_writable(errno, io, Qnil);
return ret ? Qtrue : Qfalse;
}
+VALUE io_spec_rb_io_maybe_wait_writable(VALUE self, VALUE error, VALUE io, VALUE timeout) {
+ int ret = rb_io_maybe_wait_writable(NUM2INT(error), io, timeout);
+ return INT2NUM(ret);
+}
+
+#ifdef SET_NON_BLOCKING_FAILS_ALWAYS
+NORETURN(VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p));
+#endif
+
+VALUE io_spec_rb_io_maybe_wait_readable(VALUE self, VALUE error, VALUE io, VALUE timeout, VALUE read_p) {
+ int fd = io_spec_get_fd(io);
+#ifndef SET_NON_BLOCKING_FAILS_ALWAYS
+ char buf[RB_IO_WAIT_READABLE_BUF];
+ int ret, saved_errno;
+#endif
+
+ if (set_non_blocking(fd) == -1)
+ rb_sys_fail("set_non_blocking failed");
+
+#ifndef SET_NON_BLOCKING_FAILS_ALWAYS
+ if (RTEST(read_p)) {
+ if (read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) {
+ return Qnil;
+ }
+ saved_errno = errno;
+ rb_ivar_set(self, rb_intern("@write_data"), Qtrue);
+ errno = saved_errno;
+ }
+
+ // main part
+ ret = rb_io_maybe_wait_readable(NUM2INT(error), io, timeout);
+
+ if (RTEST(read_p)) {
+ ssize_t r = read(fd, buf, RB_IO_WAIT_READABLE_BUF);
+ if (r != RB_IO_WAIT_READABLE_BUF) {
+ perror("read");
+ return SSIZET2NUM(r);
+ }
+ rb_ivar_set(self, rb_intern("@read_data"),
+ rb_str_new(buf, RB_IO_WAIT_READABLE_BUF));
+ }
+
+ return INT2NUM(ret);
+#else
+ UNREACHABLE_RETURN(Qnil);
+#endif
+}
+
+VALUE io_spec_rb_io_maybe_wait(VALUE self, VALUE error, VALUE io, VALUE events, VALUE timeout) {
+ return rb_io_maybe_wait(NUM2INT(error), io, events, timeout);
+}
+
VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) {
rb_thread_wait_fd(io_spec_get_fd(io));
return Qnil;
}
VALUE io_spec_rb_wait_for_single_fd(VALUE self, VALUE io, VALUE events, VALUE secs, VALUE usecs) {
- int fd = io_spec_get_fd(io);
- struct timeval tv;
+ VALUE timeout = Qnil;
if (!NIL_P(secs)) {
- tv.tv_sec = FIX2INT(secs);
- tv.tv_usec = FIX2INT(usecs);
+ timeout = rb_float_new((double)FIX2INT(secs) + (0.000001 * FIX2INT(usecs)));
}
- return INT2FIX(rb_wait_for_single_fd(fd, FIX2INT(events), NIL_P(secs) ? NULL : &tv));
+ VALUE result = rb_io_wait(io, events, timeout);
+ if (result == Qfalse) return INT2FIX(0);
+ else return result;
}
VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) {
@@ -249,7 +299,7 @@ VALUE io_spec_rb_io_set_nonblock(VALUE self, VALUE io) {
GetOpenFile(io, fp);
rb_io_set_nonblock(fp);
#ifdef F_GETFL
- flags = fcntl(fp->fd, F_GETFL, 0);
+ flags = fcntl(io_spec_get_fd(io), F_GETFL, 0);
return flags & O_NONBLOCK ? Qtrue : Qfalse;
#else
return Qfalse;
@@ -268,15 +318,50 @@ static VALUE io_spec_errno_set(VALUE self, VALUE val) {
}
VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) {
+ int mode;
+#ifdef RUBY_VERSION_IS_3_3
+ mode = rb_io_mode(io);
+#else
rb_io_t *fp;
GetOpenFile(io, fp);
- if (fp->mode & FMODE_SYNC) {
+ mode = fp->mode;
+#endif
+ if (mode & FMODE_SYNC) {
return Qtrue;
} else {
return Qfalse;
}
}
+#if defined(RUBY_VERSION_IS_3_3) || defined(TRUFFLERUBY)
+static VALUE io_spec_rb_io_mode(VALUE self, VALUE io) {
+ return INT2FIX(rb_io_mode(io));
+}
+
+static VALUE io_spec_rb_io_path(VALUE self, VALUE io) {
+ return rb_io_path(io);
+}
+
+static VALUE io_spec_rb_io_closed_p(VALUE self, VALUE io) {
+ return rb_io_closed_p(io);
+}
+
+static VALUE io_spec_rb_io_open_descriptor(VALUE self, VALUE klass, VALUE descriptor, VALUE mode, VALUE path, VALUE timeout, VALUE internal_encoding, VALUE external_encoding, VALUE ecflags, VALUE ecopts) {
+ struct rb_io_encoding io_encoding;
+
+ io_encoding.enc = rb_to_encoding(internal_encoding);
+ io_encoding.enc2 = rb_to_encoding(external_encoding);
+ io_encoding.ecflags = FIX2INT(ecflags);
+ io_encoding.ecopts = ecopts;
+
+ return rb_io_open_descriptor(klass, FIX2INT(descriptor), FIX2INT(mode), path, timeout, &io_encoding);
+}
+
+static VALUE io_spec_rb_io_open_descriptor_without_encoding(VALUE self, VALUE klass, VALUE descriptor, VALUE mode, VALUE path, VALUE timeout) {
+ return rb_io_open_descriptor(klass, FIX2INT(descriptor), FIX2INT(mode), path, timeout, NULL);
+}
+#endif
+
void Init_io_spec(void) {
VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject);
rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1);
@@ -294,6 +379,9 @@ void Init_io_spec(void) {
rb_define_method(cls, "rb_io_taint_check", io_spec_rb_io_taint_check, 1);
rb_define_method(cls, "rb_io_wait_readable", io_spec_rb_io_wait_readable, 2);
rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1);
+ rb_define_method(cls, "rb_io_maybe_wait_writable", io_spec_rb_io_maybe_wait_writable, 3);
+ rb_define_method(cls, "rb_io_maybe_wait_readable", io_spec_rb_io_maybe_wait_readable, 4);
+ rb_define_method(cls, "rb_io_maybe_wait", io_spec_rb_io_maybe_wait, 4);
rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1);
rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1);
rb_define_method(cls, "rb_thread_fd_select_read", io_spec_rb_thread_fd_select_read, 1);
@@ -305,6 +393,18 @@ void Init_io_spec(void) {
rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3);
rb_define_method(cls, "errno=", io_spec_errno_set, 1);
rb_define_method(cls, "rb_io_mode_sync_flag", io_spec_mode_sync_flag, 1);
+#if defined(RUBY_VERSION_IS_3_3) || defined(TRUFFLERUBY)
+ rb_define_method(cls, "rb_io_mode", io_spec_rb_io_mode, 1);
+ rb_define_method(cls, "rb_io_path", io_spec_rb_io_path, 1);
+ rb_define_method(cls, "rb_io_closed_p", io_spec_rb_io_closed_p, 1);
+ rb_define_method(cls, "rb_io_open_descriptor", io_spec_rb_io_open_descriptor, 9);
+ rb_define_method(cls, "rb_io_open_descriptor_without_encoding", io_spec_rb_io_open_descriptor_without_encoding, 5);
+ rb_define_const(cls, "FMODE_READABLE", INT2FIX(FMODE_READABLE));
+ rb_define_const(cls, "FMODE_WRITABLE", INT2FIX(FMODE_WRITABLE));
+ rb_define_const(cls, "FMODE_BINMODE", INT2FIX(FMODE_BINMODE));
+ rb_define_const(cls, "FMODE_TEXTMODE", INT2FIX(FMODE_TEXTMODE));
+ rb_define_const(cls, "ECONV_UNIVERSAL_NEWLINE_DECORATOR", INT2FIX(ECONV_UNIVERSAL_NEWLINE_DECORATOR));
+#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c
index e194ba8fde..a8fed21b59 100644
--- a/spec/ruby/optional/capi/ext/kernel_spec.c
+++ b/spec/ruby/optional/capi/ext/kernel_spec.c
@@ -7,16 +7,12 @@
extern "C" {
#endif
-VALUE kernel_spec_call_proc(VALUE arg_array) {
+static VALUE kernel_spec_call_proc(VALUE arg_array) {
VALUE arg = rb_ary_pop(arg_array);
VALUE proc = rb_ary_pop(arg_array);
return rb_funcall(proc, rb_intern("call"), 1, arg);
}
-VALUE kernel_spec_call_proc_raise(VALUE arg_array, VALUE raised_exc) {
- return kernel_spec_call_proc(arg_array);
-}
-
static VALUE kernel_spec_rb_block_given_p(VALUE self) {
return rb_block_given_p() ? Qtrue : Qfalse;
}
@@ -71,11 +67,20 @@ VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) {
return rb_block_call(ary, rb_intern("map"), 0, NULL, (rb_block_call_func_t)NULL, Qnil);
}
-
VALUE kernel_spec_rb_frame_this_func(VALUE self) {
return ID2SYM(rb_frame_this_func());
}
+VALUE kernel_spec_rb_category_warn_deprecated(VALUE self) {
+ rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "foo");
+ return Qnil;
+}
+
+VALUE kernel_spec_rb_category_warn_deprecated_with_integer_extra_value(VALUE self, VALUE value) {
+ rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "foo %d", FIX2INT(value));
+ return Qnil;
+}
+
VALUE kernel_spec_rb_ensure(VALUE self, VALUE main_proc, VALUE arg,
VALUE ensure_proc, VALUE arg2) {
VALUE main_array, ensure_array;
@@ -112,9 +117,11 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) {
return rb_eval_string(RSTRING_PTR(str));
}
+#ifndef RUBY_VERSION_IS_4_0
VALUE kernel_spec_rb_eval_cmd_kw(VALUE self, VALUE cmd, VALUE args, VALUE kw_splat) {
return rb_eval_cmd_kw(cmd, args, NUM2INT(kw_splat));
}
+#endif
VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) {
rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before")));
@@ -134,7 +141,16 @@ VALUE kernel_spec_rb_throw_obj(VALUE self, VALUE obj, VALUE result) {
return ID2SYM(rb_intern("rb_throw_failed"));
}
-VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) {
+VALUE kernel_spec_rb_errinfo(VALUE self) {
+ return rb_errinfo();
+}
+
+VALUE kernel_spec_rb_set_errinfo(VALUE self, VALUE exc) {
+ rb_set_errinfo(exc);
+ return Qnil;
+}
+
+static VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) {
VALUE argv[2];
int argc;
@@ -181,7 +197,7 @@ VALUE kernel_spec_rb_rescue2(int argc, VALUE *args, VALUE self) {
rb_ary_push(raise_array, args[3]);
return rb_rescue2(kernel_spec_call_proc, main_array,
- kernel_spec_call_proc_raise, raise_array, args[4], args[5], (VALUE)0);
+ kernel_spec_call_proc_with_raised_exc, raise_array, args[4], args[5], (VALUE)0);
}
static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) {
@@ -195,7 +211,7 @@ static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) {
return res;
}
-static VALUE kernel_spec_rb_protect_errinfo(VALUE self, VALUE obj, VALUE ary) {
+static VALUE kernel_spec_rb_protect_ignore_status(VALUE self, VALUE obj, VALUE ary) {
int status = 0;
VALUE res = rb_protect(rb_yield, obj, &status);
rb_ary_store(ary, 0, INT2NUM(23));
@@ -220,8 +236,8 @@ static VALUE kernel_spec_rb_eval_string_protect(VALUE self, VALUE str, VALUE ary
VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
errno = 1;
- if(msg == Qnil) {
- rb_sys_fail(0);
+ if (msg == Qnil) {
+ rb_sys_fail(NULL);
} else if (self != Qundef) {
rb_sys_fail(StringValuePtr(msg));
}
@@ -229,7 +245,7 @@ VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
}
VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) {
- if(msg == Qnil) {
+ if (msg == Qnil) {
rb_syserr_fail(NUM2INT(err), NULL);
} else if (self != Qundef) {
rb_syserr_fail(NUM2INT(err), StringValuePtr(msg));
@@ -237,6 +253,13 @@ VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) {
return Qnil;
}
+VALUE kernel_spec_rb_syserr_fail_str(VALUE self, VALUE err, VALUE msg) {
+ if (self != Qundef) {
+ rb_syserr_fail_str(NUM2INT(err), msg);
+ }
+ return Qnil;
+}
+
VALUE kernel_spec_rb_warn(VALUE self, VALUE msg) {
rb_warn("%s", StringValuePtr(msg));
return Qnil;
@@ -292,9 +315,9 @@ static VALUE kernel_spec_rb_yield_values2(VALUE self, VALUE ary) {
}
static VALUE do_rec(VALUE obj, VALUE arg, int is_rec) {
- if(is_rec) {
+ if (is_rec) {
return obj;
- } else if(arg == Qtrue) {
+ } else if (arg == Qtrue) {
return rb_exec_recursive(do_rec, obj, Qnil);
} else {
return Qnil;
@@ -318,6 +341,10 @@ static VALUE kernel_spec_rb_f_sprintf(VALUE self, VALUE ary) {
return rb_f_sprintf((int)RARRAY_LEN(ary), RARRAY_PTR(ary));
}
+static VALUE kernel_spec_rb_str_format(VALUE self, VALUE count, VALUE ary, VALUE format) {
+ return rb_str_format(FIX2INT(count), RARRAY_PTR(ary), format);
+}
+
static VALUE kernel_spec_rb_make_backtrace(VALUE self) {
return rb_make_backtrace();
}
@@ -326,7 +353,6 @@ static VALUE kernel_spec_rb_funcallv(VALUE self, VALUE obj, VALUE method, VALUE
return rb_funcallv(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args));
}
-#ifdef RUBY_VERSION_IS_3_0
static VALUE kernel_spec_rb_funcallv_kw(VALUE self, VALUE obj, VALUE method, VALUE args) {
return rb_funcallv_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), RB_PASS_KEYWORDS);
}
@@ -334,14 +360,17 @@ static VALUE kernel_spec_rb_funcallv_kw(VALUE self, VALUE obj, VALUE method, VAL
static VALUE kernel_spec_rb_keyword_given_p(int argc, VALUE *args, VALUE self) {
return rb_keyword_given_p() ? Qtrue : Qfalse;
}
-#endif
static VALUE kernel_spec_rb_funcallv_public(VALUE self, VALUE obj, VALUE method) {
return rb_funcallv_public(obj, SYM2ID(method), 0, NULL);
}
-static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) {
- return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block);
+static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
+ return rb_funcall_with_block(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block);
+}
+
+static VALUE kernel_spec_rb_funcall_with_block_kw(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
+ return rb_funcall_with_block_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
}
static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE method) {
@@ -351,6 +380,15 @@ static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE metho
INT2FIX(5), INT2FIX(4), INT2FIX(3), INT2FIX(2), INT2FIX(1));
}
+static VALUE kernel_spec_rb_check_funcall(VALUE self, VALUE receiver, VALUE method, VALUE args) {
+ VALUE ret = rb_check_funcall(receiver, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args));
+ if (ret == Qundef) {
+ return ID2SYM(rb_intern("Qundef"));
+ } else {
+ return ret;
+ }
+}
+
void Init_kernel_spec(void) {
VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject);
rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0);
@@ -363,22 +401,30 @@ void Init_kernel_spec(void) {
rb_define_method(cls, "rb_block_lambda", kernel_spec_rb_block_lambda, 0);
rb_define_method(cls, "rb_frame_this_func_test", kernel_spec_rb_frame_this_func, 0);
rb_define_method(cls, "rb_frame_this_func_test_again", kernel_spec_rb_frame_this_func, 0);
+ rb_define_method(cls, "rb_category_warn_deprecated", kernel_spec_rb_category_warn_deprecated, 0);
+ rb_define_method(cls, "rb_category_warn_deprecated_with_integer_extra_value", kernel_spec_rb_category_warn_deprecated_with_integer_extra_value, 1);
rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4);
rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1);
+#ifndef RUBY_VERSION_IS_4_0
rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3);
+#endif
rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1);
rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1);
rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2);
+ rb_define_method(cls, "rb_errinfo", kernel_spec_rb_errinfo, 0);
+ rb_define_method(cls, "rb_set_errinfo", kernel_spec_rb_set_errinfo, 1);
+ rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4);
rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4);
rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1);
rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2);
- rb_define_method(cls, "rb_protect_errinfo", kernel_spec_rb_protect_errinfo, 2);
+ rb_define_method(cls, "rb_protect_ignore_status", kernel_spec_rb_protect_ignore_status, 2);
rb_define_method(cls, "rb_protect_null_status", kernel_spec_rb_protect_null_status, 1);
rb_define_method(cls, "rb_eval_string_protect", kernel_spec_rb_eval_string_protect, 2);
rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2);
rb_define_method(cls, "rb_catch_obj", kernel_spec_rb_catch_obj, 2);
rb_define_method(cls, "rb_sys_fail", kernel_spec_rb_sys_fail, 1);
rb_define_method(cls, "rb_syserr_fail", kernel_spec_rb_syserr_fail, 2);
+ rb_define_method(cls, "rb_syserr_fail_str", kernel_spec_rb_syserr_fail_str, 2);
rb_define_method(cls, "rb_warn", kernel_spec_rb_warn, 1);
rb_define_method(cls, "rb_yield", kernel_spec_rb_yield, 1);
rb_define_method(cls, "rb_yield_indirected", kernel_spec_rb_yield_indirected, 1);
@@ -389,15 +435,16 @@ void Init_kernel_spec(void) {
rb_define_method(cls, "rb_exec_recursive", kernel_spec_rb_exec_recursive, 1);
rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1);
rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1);
+ rb_define_method(cls, "rb_str_format", kernel_spec_rb_str_format, 3);
rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0);
rb_define_method(cls, "rb_funcallv", kernel_spec_rb_funcallv, 3);
-#ifdef RUBY_VERSION_IS_3_0
rb_define_method(cls, "rb_funcallv_kw", kernel_spec_rb_funcallv_kw, 3);
rb_define_method(cls, "rb_keyword_given_p", kernel_spec_rb_keyword_given_p, -1);
-#endif
rb_define_method(cls, "rb_funcallv_public", kernel_spec_rb_funcallv_public, 2);
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
- rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
+ rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 4);
+ rb_define_method(cls, "rb_funcall_with_block_kw", kernel_spec_rb_funcall_with_block_kw, 4);
+ rb_define_method(cls, "rb_check_funcall", kernel_spec_rb_check_funcall, 3);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/mutex_spec.c b/spec/ruby/optional/capi/ext/mutex_spec.c
index c2fdf917ac..d2c8f98e89 100644
--- a/spec/ruby/optional/capi/ext/mutex_spec.c
+++ b/spec/ruby/optional/capi/ext/mutex_spec.c
@@ -29,15 +29,34 @@ VALUE mutex_spec_rb_mutex_sleep(VALUE self, VALUE mutex, VALUE timeout) {
return rb_mutex_sleep(mutex, timeout);
}
-
VALUE mutex_spec_rb_mutex_callback(VALUE arg) {
return rb_funcall(arg, rb_intern("call"), 0);
}
+VALUE mutex_spec_rb_mutex_naughty_callback(VALUE arg) {
+ int *result = (int *) arg;
+ return (VALUE) result;
+}
+
+VALUE mutex_spec_rb_mutex_callback_basic(VALUE arg) {
+ return arg;
+}
+
VALUE mutex_spec_rb_mutex_synchronize(VALUE self, VALUE mutex, VALUE value) {
return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback, value);
}
+VALUE mutex_spec_rb_mutex_synchronize_with_naughty_callback(VALUE self, VALUE mutex) {
+ // a naughty callback accepts or returns not a Ruby object but arbitrary value
+ int arg = 42;
+ VALUE result = rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_naughty_callback, (VALUE) &arg);
+ return INT2NUM(*((int *) result));
+}
+
+VALUE mutex_spec_rb_mutex_synchronize_with_native_callback(VALUE self, VALUE mutex, VALUE value) {
+ return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback_basic, value);
+}
+
void Init_mutex_spec(void) {
VALUE cls = rb_define_class("CApiMutexSpecs", rb_cObject);
rb_define_method(cls, "rb_mutex_new", mutex_spec_rb_mutex_new, 0);
@@ -47,6 +66,8 @@ void Init_mutex_spec(void) {
rb_define_method(cls, "rb_mutex_unlock", mutex_spec_rb_mutex_unlock, 1);
rb_define_method(cls, "rb_mutex_sleep", mutex_spec_rb_mutex_sleep, 2);
rb_define_method(cls, "rb_mutex_synchronize", mutex_spec_rb_mutex_synchronize, 2);
+ rb_define_method(cls, "rb_mutex_synchronize_with_naughty_callback", mutex_spec_rb_mutex_synchronize_with_naughty_callback, 1);
+ rb_define_method(cls, "rb_mutex_synchronize_with_native_callback", mutex_spec_rb_mutex_synchronize_with_native_callback, 2);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c
index 30ac44cf1f..995bc38fcf 100644
--- a/spec/ruby/optional/capi/ext/object_spec.c
+++ b/spec/ruby/optional/capi/ext/object_spec.c
@@ -15,11 +15,6 @@ static VALUE object_spec_FL_ABLE(VALUE self, VALUE obj) {
static int object_spec_FL_TEST_flag(VALUE flag_string) {
char *flag_cstr = StringValueCStr(flag_string);
-#ifndef RUBY_VERSION_IS_3_1
- if (strcmp(flag_cstr, "FL_TAINT") == 0) {
- return FL_TAINT;
- }
-#endif
if (strcmp(flag_cstr, "FL_FREEZE") == 0) {
return FL_FREEZE;
}
@@ -30,22 +25,6 @@ static VALUE object_spec_FL_TEST(VALUE self, VALUE obj, VALUE flag) {
return INT2FIX(FL_TEST(obj, object_spec_FL_TEST_flag(flag)));
}
-#ifndef RUBY_VERSION_IS_3_1
-static VALUE object_spec_OBJ_TAINT(VALUE self, VALUE obj) {
- OBJ_TAINT(obj);
- return Qnil;
-}
-
-static VALUE object_spec_OBJ_TAINTED(VALUE self, VALUE obj) {
- return OBJ_TAINTED(obj) ? Qtrue : Qfalse;
-}
-
-static VALUE object_spec_OBJ_INFECT(VALUE self, VALUE host, VALUE source) {
- OBJ_INFECT(host, source);
- return Qnil;
-}
-#endif
-
static VALUE object_spec_rb_any_to_s(VALUE self, VALUE obj) {
return rb_any_to_s(obj);
}
@@ -154,30 +133,6 @@ static VALUE object_specs_rb_obj_method(VALUE self, VALUE obj, VALUE method) {
return rb_obj_method(obj, method);
}
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-declarations"
-# endif
-#endif
-
-#ifndef RUBY_VERSION_IS_3_2
-static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) {
- return rb_obj_taint(obj);
-}
-#endif
-
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic pop
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic pop
-# endif
-#endif
-
static VALUE so_require(VALUE self) {
rb_require("fixtures/foo");
return Qnil;
@@ -197,11 +152,7 @@ static VALUE object_spec_rb_method_boundp(VALUE self, VALUE obj, VALUE method, V
}
static VALUE object_spec_rb_special_const_p(VALUE self, VALUE value) {
- if (rb_special_const_p(value)) {
- return Qtrue;
- } else {
- return Qfalse;
- }
+ return rb_special_const_p(value);
}
static VALUE so_to_id(VALUE self, VALUE obj) {
@@ -218,126 +169,126 @@ static VALUE so_check_type(VALUE self, VALUE obj, VALUE other) {
}
static VALUE so_is_type_nil(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_NIL) {
+ if (TYPE(obj) == T_NIL) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_object(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_OBJECT) {
+ if (TYPE(obj) == T_OBJECT) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_array(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_ARRAY) {
+ if (TYPE(obj) == T_ARRAY) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_module(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_MODULE) {
+ if (TYPE(obj) == T_MODULE) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_class(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_CLASS) {
+ if (TYPE(obj) == T_CLASS) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_type_data(VALUE self, VALUE obj) {
- if(TYPE(obj) == T_DATA) {
+ if (TYPE(obj) == T_DATA) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_nil(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_NIL)) {
+ if (rb_type_p(obj, T_NIL)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_object(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_OBJECT)) {
+ if (rb_type_p(obj, T_OBJECT)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_array(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_ARRAY)) {
+ if (rb_type_p(obj, T_ARRAY)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_module(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_MODULE)) {
+ if (rb_type_p(obj, T_MODULE)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_class(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_CLASS)) {
+ if (rb_type_p(obj, T_CLASS)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_DATA)) {
+ if (rb_type_p(obj, T_DATA)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_rb_type_p_file(VALUE self, VALUE obj) {
- if(rb_type_p(obj, T_FILE)) {
+ if (rb_type_p(obj, T_FILE)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_OBJECT) {
+ if (BUILTIN_TYPE(obj) == T_OBJECT) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_array(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_ARRAY) {
+ if (BUILTIN_TYPE(obj) == T_ARRAY) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_module(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_MODULE) {
+ if (BUILTIN_TYPE(obj) == T_MODULE) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_class(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_CLASS) {
+ if (BUILTIN_TYPE(obj) == T_CLASS) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_data(VALUE self, VALUE obj) {
- if(BUILTIN_TYPE(obj) == T_DATA) {
+ if (BUILTIN_TYPE(obj) == T_DATA) {
return Qtrue;
}
return Qfalse;
@@ -406,49 +357,46 @@ static VALUE object_spec_rb_ivar_foreach(VALUE self, VALUE obj) {
}
static VALUE speced_allocator(VALUE klass) {
- VALUE flags = 0;
- VALUE instance;
- if (RTEST(rb_class_inherited_p(klass, rb_cString))) {
- flags = T_STRING;
- } else if (RTEST(rb_class_inherited_p(klass, rb_cArray))) {
- flags = T_ARRAY;
- } else {
- flags = T_OBJECT;
- }
- instance = rb_newobj_of(klass, flags);
+ VALUE super = rb_class_get_superclass(klass);
+ VALUE instance = rb_get_alloc_func(super)(klass);
rb_iv_set(instance, "@from_custom_allocator", Qtrue);
return instance;
}
-static VALUE define_alloc_func(VALUE self, VALUE klass) {
+static VALUE object_spec_rb_define_alloc_func(VALUE self, VALUE klass) {
rb_define_alloc_func(klass, speced_allocator);
return Qnil;
}
-static VALUE undef_alloc_func(VALUE self, VALUE klass) {
+static VALUE object_spec_rb_undef_alloc_func(VALUE self, VALUE klass) {
rb_undef_alloc_func(klass);
return Qnil;
}
-static VALUE speced_allocator_p(VALUE self, VALUE klass) {
+static VALUE object_spec_speced_allocator_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return (allocator == speced_allocator) ? Qtrue : Qfalse;
}
-static VALUE custom_alloc_func_p(VALUE self, VALUE klass) {
+static VALUE object_spec_custom_alloc_func_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return allocator ? Qtrue : Qfalse;
}
+static VALUE object_spec_redefine_frozen(VALUE self) {
+ // The purpose of this spec is to verify that `frozen?`
+ // and `RB_OBJ_FROZEN` do not mutually recurse infinitely.
+ if (RB_OBJ_FROZEN(self)) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
void Init_object_spec(void) {
VALUE cls = rb_define_class("CApiObjectSpecs", rb_cObject);
rb_define_method(cls, "FL_ABLE", object_spec_FL_ABLE, 1);
rb_define_method(cls, "FL_TEST", object_spec_FL_TEST, 2);
-#ifndef RUBY_VERSION_IS_3_1
- rb_define_method(cls, "OBJ_TAINT", object_spec_OBJ_TAINT, 1);
- rb_define_method(cls, "OBJ_TAINTED", object_spec_OBJ_TAINTED, 1);
- rb_define_method(cls, "OBJ_INFECT", object_spec_OBJ_INFECT, 2);
-#endif
rb_define_method(cls, "rb_any_to_s", object_spec_rb_any_to_s, 1);
rb_define_method(cls, "rb_attr_get", so_attr_get, 2);
rb_define_method(cls, "rb_obj_instance_variables", object_spec_rb_obj_instance_variables, 1);
@@ -473,15 +421,11 @@ void Init_object_spec(void) {
rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2);
rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2);
rb_define_method(cls, "rb_obj_method", object_specs_rb_obj_method, 2);
-#ifndef RUBY_VERSION_IS_3_2
- rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1);
-#endif
rb_define_method(cls, "rb_require", so_require, 0);
rb_define_method(cls, "rb_respond_to", so_respond_to, 2);
rb_define_method(cls, "rb_method_boundp", object_spec_rb_method_boundp, 3);
rb_define_method(cls, "rb_obj_respond_to", so_obj_respond_to, 3);
rb_define_method(cls, "rb_special_const_p", object_spec_rb_special_const_p, 1);
-
rb_define_method(cls, "rb_to_id", so_to_id, 1);
rb_define_method(cls, "RTEST", object_spec_RTEST, 1);
rb_define_method(cls, "rb_check_type", so_check_type, 2);
@@ -515,12 +459,15 @@ void Init_object_spec(void) {
rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2);
rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2);
rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1);
- rb_define_method(cls, "rb_define_alloc_func", define_alloc_func, 1);
- rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1);
- rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1);
- rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1);
+ rb_define_method(cls, "rb_define_alloc_func", object_spec_rb_define_alloc_func, 1);
+ rb_define_method(cls, "rb_undef_alloc_func", object_spec_rb_undef_alloc_func, 1);
+ rb_define_method(cls, "speced_allocator?", object_spec_speced_allocator_p, 1);
+ rb_define_method(cls, "custom_alloc_func?", object_spec_custom_alloc_func_p, 1);
rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1);
rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1);
+
+ cls = rb_define_class("CApiObjectRedefinitionSpecs", rb_cObject);
+ rb_define_method(cls, "frozen?", object_spec_redefine_frozen, 0);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/proc_spec.c b/spec/ruby/optional/capi/ext/proc_spec.c
index 1137f4156b..b7cd5d6262 100644
--- a/spec/ruby/optional/capi/ext/proc_spec.c
+++ b/spec/ruby/optional/capi/ext/proc_spec.c
@@ -76,6 +76,18 @@ VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) {
return rb_proc_call(prc, args);
}
+VALUE proc_spec_rb_proc_call_kw(VALUE self, VALUE prc, VALUE args) {
+ return rb_proc_call_kw(prc, args, RB_PASS_KEYWORDS);
+}
+
+VALUE proc_spec_rb_proc_call_with_block(VALUE self, VALUE prc, VALUE args, VALUE block) {
+ return rb_proc_call_with_block(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block);
+}
+
+static VALUE proc_spec_rb_proc_call_with_block_kw(VALUE self, VALUE prc, VALUE args, VALUE block) {
+ return rb_proc_call_with_block_kw(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
+}
+
VALUE proc_spec_rb_obj_is_proc(VALUE self, VALUE prc) {
return rb_obj_is_proc(prc);
}
@@ -123,6 +135,9 @@ void Init_proc_spec(void) {
rb_define_method(cls, "rb_proc_new_block_given_p", proc_spec_rb_proc_new_block_given_p, 0);
rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1);
rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2);
+ rb_define_method(cls, "rb_proc_call_kw", proc_spec_rb_proc_call_kw, 2);
+ rb_define_method(cls, "rb_proc_call_with_block", proc_spec_rb_proc_call_with_block, 3);
+ rb_define_method(cls, "rb_proc_call_with_block_kw", proc_spec_rb_proc_call_with_block_kw, 3);
rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1);
rb_define_method(cls, "rb_obj_is_proc", proc_spec_rb_obj_is_proc, 1);
}
diff --git a/spec/ruby/optional/capi/ext/range_spec.c b/spec/ruby/optional/capi/ext/range_spec.c
index 7a475ec695..9faed3e5ee 100644
--- a/spec/ruby/optional/capi/ext/range_spec.c
+++ b/spec/ruby/optional/capi/ext/range_spec.c
@@ -7,7 +7,7 @@ extern "C" {
VALUE range_spec_rb_range_new(int argc, VALUE* argv, VALUE self) {
int exclude_end = 0;
- if(argc == 3) {
+ if (argc == 3) {
exclude_end = RTEST(argv[2]);
}
return rb_range_new(argv[0], argv[1], exclude_end);
@@ -25,9 +25,9 @@ VALUE range_spec_rb_range_values(VALUE self, VALUE range) {
return ary;
}
-VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE lenpv, VALUE lenv, VALUE errv) {
- long begp = FIX2LONG(begpv);
- long lenp = FIX2LONG(lenpv);
+VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE lenv, VALUE errv) {
+ long begp = 0;
+ long lenp = 0;
long len = FIX2LONG(lenv);
int err = FIX2INT(errv);
VALUE ary = rb_ary_new();
@@ -38,11 +38,51 @@ VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE le
return ary;
}
+VALUE range_spec_rb_arithmetic_sequence_extract(VALUE self, VALUE object) {
+ VALUE ary = rb_ary_new();
+ rb_arithmetic_sequence_components_t components;
+
+ int status = rb_arithmetic_sequence_extract(object, &components);
+
+ if (!status) {
+ rb_ary_store(ary, 0, LONG2FIX(status));
+ return ary;
+ }
+
+ rb_ary_store(ary, 0, LONG2FIX(status));
+ rb_ary_store(ary, 1, components.begin);
+ rb_ary_store(ary, 2, components.end);
+ rb_ary_store(ary, 3, components.step);
+ rb_ary_store(ary, 4, components.exclude_end ? Qtrue : Qfalse);
+ return ary;
+}
+
+VALUE range_spec_rb_arithmetic_sequence_beg_len_step(VALUE self, VALUE aseq, VALUE lenv, VALUE errv) {
+ long begp = 0;
+ long lenp = 0;
+ long stepp = 0;
+
+ long len = FIX2LONG(lenv);
+ int err = FIX2INT(errv);
+
+ VALUE success = rb_arithmetic_sequence_beg_len_step(aseq, &begp, &lenp, &stepp, len, err);
+
+ VALUE ary = rb_ary_new();
+ rb_ary_store(ary, 0, success);
+ rb_ary_store(ary, 1, LONG2FIX(begp));
+ rb_ary_store(ary, 2, LONG2FIX(lenp));
+ rb_ary_store(ary, 3, LONG2FIX(stepp));
+
+ return ary;
+}
+
void Init_range_spec(void) {
VALUE cls = rb_define_class("CApiRangeSpecs", rb_cObject);
rb_define_method(cls, "rb_range_new", range_spec_rb_range_new, -1);
rb_define_method(cls, "rb_range_values", range_spec_rb_range_values, 1);
- rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 5);
+ rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 3);
+ rb_define_method(cls, "rb_arithmetic_sequence_extract", range_spec_rb_arithmetic_sequence_extract, 1);
+ rb_define_method(cls, "rb_arithmetic_sequence_beg_len_step", range_spec_rb_arithmetic_sequence_beg_len_step, 3);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/rbasic_spec.c b/spec/ruby/optional/capi/ext/rbasic_spec.c
index 9178e5f639..5a95b92804 100644
--- a/spec/ruby/optional/capi/ext/rbasic_spec.c
+++ b/spec/ruby/optional/capi/ext/rbasic_spec.c
@@ -5,6 +5,14 @@
extern "C" {
#endif
+#ifndef RBASIC_FLAGS
+#define RBASIC_FLAGS(obj) (RBASIC(obj)->flags)
+#endif
+
+#ifndef RBASIC_SET_FLAGS
+#define RBASIC_SET_FLAGS(obj, flags_to_set) (RBASIC(obj)->flags = flags_to_set)
+#endif
+
#ifndef FL_SHAREABLE
static const VALUE VISIBLE_BITS = FL_TAINT | FL_FREEZE;
static const VALUE DATA_VISIBLE_BITS = FL_TAINT | FL_FREEZE | ~(FL_USER0 - 1);
@@ -23,65 +31,61 @@ static const VALUE DATA_VISIBLE_BITS = FL_FREEZE | ~(FL_USER0 - 1);
#error "unsupported"
#endif
-
-#ifndef RUBY_VERSION_IS_3_1
-VALUE rbasic_spec_taint_flag(VALUE self) {
- return VALUE2NUM(RUBY_FL_TAINT);
-}
-#endif
-
VALUE rbasic_spec_freeze_flag(VALUE self) {
return VALUE2NUM(RUBY_FL_FREEZE);
}
- static VALUE spec_get_flags(const struct RBasic *b, VALUE visible_bits) {
- VALUE flags = b->flags & visible_bits;
+static VALUE spec_get_flags(VALUE obj, VALUE visible_bits) {
+ VALUE flags = RB_FL_TEST(obj, visible_bits);
return VALUE2NUM(flags);
}
-static VALUE spec_set_flags(struct RBasic *b, VALUE flags, VALUE visible_bits) {
+static VALUE spec_set_flags(VALUE obj, VALUE flags, VALUE visible_bits) {
flags &= visible_bits;
- b->flags = (b->flags & ~visible_bits) | flags;
+
+ // Could also be done like:
+ // RB_FL_UNSET(obj, visible_bits);
+ // RB_FL_SET(obj, flags);
+ // But that seems rather indirect
+ RBASIC_SET_FLAGS(obj, (RBASIC_FLAGS(obj) & ~visible_bits) | flags);
+
return VALUE2NUM(flags);
}
-VALUE rbasic_spec_get_flags(VALUE self, VALUE val) {
- return spec_get_flags(RBASIC(val), VISIBLE_BITS);
+static VALUE rbasic_spec_get_flags(VALUE self, VALUE obj) {
+ return spec_get_flags(obj, VISIBLE_BITS);
}
-VALUE rbasic_spec_set_flags(VALUE self, VALUE val, VALUE flags) {
- return spec_set_flags(RBASIC(val), NUM2VALUE(flags), VISIBLE_BITS);
+static VALUE rbasic_spec_set_flags(VALUE self, VALUE obj, VALUE flags) {
+ return spec_set_flags(obj, NUM2VALUE(flags), VISIBLE_BITS);
}
-VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
- return spec_set_flags(RBASIC(to), RBASIC(from)->flags, VISIBLE_BITS);
+static VALUE rbasic_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
+ return spec_set_flags(to, RBASIC_FLAGS(from), VISIBLE_BITS);
}
-VALUE rbasic_spec_get_klass(VALUE self, VALUE val) {
- return RBASIC(val)->klass;
+static VALUE rbasic_spec_get_klass(VALUE self, VALUE obj) {
+ return RBASIC_CLASS(obj);
}
-VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) {
- return spec_get_flags(&RDATA(structure)->basic, DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_get_flags(VALUE self, VALUE structure) {
+ return spec_get_flags(structure, DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) {
- return spec_set_flags(&RDATA(structure)->basic, NUM2VALUE(flags), DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_set_flags(VALUE self, VALUE structure, VALUE flags) {
+ return spec_set_flags(structure, NUM2VALUE(flags), DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
- return spec_set_flags(&RDATA(to)->basic, RDATA(from)->basic.flags, DATA_VISIBLE_BITS);
+static VALUE rbasic_rdata_spec_copy_flags(VALUE self, VALUE to, VALUE from) {
+ return spec_set_flags(to, RBASIC_FLAGS(from), DATA_VISIBLE_BITS);
}
-VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) {
- return RDATA(structure)->basic.klass;
+static VALUE rbasic_rdata_spec_get_klass(VALUE self, VALUE structure) {
+ return RBASIC_CLASS(structure);
}
void Init_rbasic_spec(void) {
VALUE cls = rb_define_class("CApiRBasicSpecs", rb_cObject);
-#ifndef RUBY_VERSION_IS_3_1
- rb_define_method(cls, "taint_flag", rbasic_spec_taint_flag, 0);
-#endif
rb_define_method(cls, "freeze_flag", rbasic_spec_freeze_flag, 0);
rb_define_method(cls, "get_flags", rbasic_spec_get_flags, 1);
rb_define_method(cls, "set_flags", rbasic_spec_set_flags, 2);
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
index 80deca24c6..6c4bea5da0 100644
--- a/spec/ruby/optional/capi/ext/rubyspec.h
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -5,38 +5,46 @@
* guards to assist with version incompatibilities. */
#include <ruby.h>
-#ifdef HAVE_RUBY_VERSION_H
-# include <ruby/version.h>
-#else
-# include <version.h>
-#endif
+#include <ruby/version.h>
-#ifndef RUBY_VERSION_MAJOR
-#define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR
-#define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
-#define RUBY_VERSION_TEENY RUBY_API_VERSION_TEENY
-#endif
+/* copied from ext/-test-/cxxanyargs/cxxanyargs.cpp */
+#if 0 /* Ignore deprecation warnings */
-#define RUBY_VERSION_BEFORE(major,minor,teeny) \
- ((RUBY_VERSION_MAJOR < (major)) || \
- (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \
- (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny)))
-#define RUBY_VERSION_SINCE(major,minor,teeny) (!RUBY_VERSION_BEFORE(major, minor, teeny))
+#elif defined(_MSC_VER)
+#pragma warning(disable : 4996)
+
+#elif defined(__INTEL_COMPILER)
+#pragma warning(disable : 1786)
+
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+#elif defined(__SUNPRO_CC)
+#pragma error_messages (off,symdeprecated)
+
+#else
+// :FIXME: improve here for your compiler.
-#if RUBY_VERSION_SINCE(3, 3, 0)
-#define RUBY_VERSION_IS_3_3
#endif
-#if RUBY_VERSION_SINCE(3, 2, 0)
-#define RUBY_VERSION_IS_3_2
+#define RUBY_VERSION_BEFORE(major,minor) \
+ ((RUBY_API_VERSION_MAJOR < (major)) || \
+ (RUBY_API_VERSION_MAJOR == (major) && RUBY_API_VERSION_MINOR < (minor)))
+#define RUBY_VERSION_SINCE(major,minor) (!RUBY_VERSION_BEFORE(major, minor))
+
+#if RUBY_VERSION_SINCE(4, 0)
+#define RUBY_VERSION_IS_4_0
#endif
-#if RUBY_VERSION_SINCE(3, 1, 0)
-#define RUBY_VERSION_IS_3_1
+#if RUBY_VERSION_SINCE(3, 4)
+#define RUBY_VERSION_IS_3_4
#endif
-#if RUBY_VERSION_SINCE(3, 0, 0)
-#define RUBY_VERSION_IS_3_0
+#if RUBY_VERSION_SINCE(3, 3)
+#define RUBY_VERSION_IS_3_3
#endif
#endif
diff --git a/spec/ruby/optional/capi/ext/set_spec.c b/spec/ruby/optional/capi/ext/set_spec.c
new file mode 100644
index 0000000000..11a271b361
--- /dev/null
+++ b/spec/ruby/optional/capi/ext/set_spec.c
@@ -0,0 +1,65 @@
+#include "ruby.h"
+#include "rubyspec.h"
+
+#ifdef RUBY_VERSION_IS_4_0
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RBOOL(x) ((x) ? Qtrue : Qfalse)
+
+int yield_element_and_arg(VALUE element, VALUE arg) {
+ return RTEST(rb_yield_values(2, element, arg)) ? ST_CONTINUE : ST_STOP;
+}
+
+VALUE set_spec_rb_set_foreach(VALUE self, VALUE set, VALUE arg) {
+ rb_set_foreach(set, yield_element_and_arg, arg);
+ return Qnil;
+}
+
+VALUE set_spec_rb_set_new(VALUE self) {
+ return rb_set_new();
+}
+
+VALUE set_spec_rb_set_new_capa(VALUE self, VALUE capa) {
+ return rb_set_new_capa(NUM2INT(capa));
+}
+
+VALUE set_spec_rb_set_lookup(VALUE self, VALUE set, VALUE element) {
+ return RBOOL(rb_set_lookup(set, element));
+}
+
+VALUE set_spec_rb_set_add(VALUE self, VALUE set, VALUE element) {
+ return RBOOL(rb_set_add(set, element));
+}
+
+VALUE set_spec_rb_set_clear(VALUE self, VALUE set) {
+ return rb_set_clear(set);
+}
+
+VALUE set_spec_rb_set_delete(VALUE self, VALUE set, VALUE element) {
+ return RBOOL(rb_set_delete(set, element));
+}
+
+VALUE set_spec_rb_set_size(VALUE self, VALUE set) {
+ return SIZET2NUM(rb_set_size(set));
+}
+
+void Init_set_spec(void) {
+ VALUE cls = rb_define_class("CApiSetSpecs", rb_cObject);
+
+ rb_define_method(cls, "rb_set_foreach", set_spec_rb_set_foreach, 2);
+ rb_define_method(cls, "rb_set_new", set_spec_rb_set_new, 0);
+ rb_define_method(cls, "rb_set_new_capa", set_spec_rb_set_new_capa, 1);
+ rb_define_method(cls, "rb_set_lookup", set_spec_rb_set_lookup, 2);
+ rb_define_method(cls, "rb_set_add", set_spec_rb_set_add, 2);
+ rb_define_method(cls, "rb_set_clear", set_spec_rb_set_clear, 1);
+ rb_define_method(cls, "rb_set_delete", set_spec_rb_set_delete, 2);
+ rb_define_method(cls, "rb_set_size", set_spec_rb_set_size, 1);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c
index 9cbb50484d..094013e049 100644
--- a/spec/ruby/optional/capi/ext/string_spec.c
+++ b/spec/ruby/optional/capi/ext/string_spec.c
@@ -51,18 +51,12 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) {
return INT2FIX(RSTRING_LEN(str));
}
-VALUE rb_fstring(VALUE str); /* internal.h, used in ripper */
-
-VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) {
- return rb_fstring(str);
-}
-
VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) {
VALUE buf;
buf = rb_str_buf_new(NUM2LONG(len));
- if(RTEST(str)) {
+ if (RTEST(str)) {
snprintf(RSTRING_PTR(buf), NUM2LONG(len), "%s", RSTRING_PTR(str));
}
@@ -123,13 +117,17 @@ VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) {
return INT2NUM(rb_str_cmp(str1, str2));
}
+VALUE string_spec_rb_str_strlen(VALUE self, VALUE str) {
+ return LONG2NUM(rb_str_strlen(str));
+}
+
VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) {
rb_encoding* from_enc;
rb_encoding* to_enc;
from_enc = rb_to_encoding(from);
- if(NIL_P(to)) {
+ if (NIL_P(to)) {
to_enc = 0;
} else {
to_enc = rb_to_encoding(to);
@@ -139,14 +137,13 @@ VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) {
}
VALUE string_spec_rb_str_conv_enc_opts(VALUE self, VALUE str, VALUE from, VALUE to,
- VALUE ecflags, VALUE ecopts)
-{
+ VALUE ecflags, VALUE ecopts) {
rb_encoding* from_enc;
rb_encoding* to_enc;
from_enc = rb_to_encoding(from);
- if(NIL_P(to)) {
+ if (NIL_P(to)) {
to_enc = 0;
} else {
to_enc = rb_to_encoding(to);
@@ -200,7 +197,7 @@ VALUE string_spec_rb_str_new_offset(VALUE self, VALUE str, VALUE offset, VALUE l
}
VALUE string_spec_rb_str_new2(VALUE self, VALUE str) {
- if(NIL_P(str)) {
+ if (NIL_P(str)) {
return rb_str_new2("");
} else {
return rb_str_new2(RSTRING_PTR(str));
@@ -216,7 +213,7 @@ VALUE string_spec_rb_str_export_to_enc(VALUE self, VALUE str, VALUE enc) {
}
VALUE string_spec_rb_str_new_cstr(VALUE self, VALUE str) {
- if(NIL_P(str)) {
+ if (NIL_P(str)) {
return rb_str_new_cstr("");
} else {
return rb_str_new_cstr(RSTRING_PTR(str));
@@ -255,34 +252,6 @@ VALUE string_spec_rb_str_new5(VALUE self, VALUE str, VALUE ptr, VALUE len) {
return rb_str_new5(str, RSTRING_PTR(ptr), FIX2INT(len));
}
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-declarations"
-# endif
-#endif
-
-#ifndef RUBY_VERSION_IS_3_2
-VALUE string_spec_rb_tainted_str_new(VALUE self, VALUE str, VALUE len) {
- return rb_tainted_str_new(RSTRING_PTR(str), FIX2INT(len));
-}
-
-VALUE string_spec_rb_tainted_str_new2(VALUE self, VALUE str) {
- return rb_tainted_str_new2(RSTRING_PTR(str));
-}
-#endif
-
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-# pragma GCC diagnostic pop
-#elif defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wdeprecated-declarations")
-# pragma clang diagnostic pop
-# endif
-#endif
-
VALUE string_spec_rb_str_plus(VALUE self, VALUE str1, VALUE str2) {
return rb_str_plus(str1, str2);
}
@@ -390,7 +359,7 @@ VALUE string_spec_RSTRING_PTR_set(VALUE self, VALUE str, VALUE i, VALUE chr) {
VALUE string_spec_RSTRING_PTR_after_funcall(VALUE self, VALUE str, VALUE cb) {
/* Silence gcc 4.3.2 warning about computed value not used */
- if(RSTRING_PTR(str)) { /* force it out */
+ if (RSTRING_PTR(str)) { /* force it out */
rb_funcall(cb, rb_intern("call"), 1, str);
}
@@ -471,6 +440,7 @@ static VALUE string_spec_rb_str_free(VALUE self, VALUE str) {
static VALUE string_spec_rb_sprintf1(VALUE self, VALUE str, VALUE repl) {
return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl));
}
+
static VALUE string_spec_rb_sprintf2(VALUE self, VALUE str, VALUE repl1, VALUE repl2) {
return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl1), RSTRING_PTR(repl2));
}
@@ -573,7 +543,7 @@ static VALUE string_spec_rb_utf8_str_new_cstr(VALUE self) {
}
PRINTF_ARGS(static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...), 2, 3);
-static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...){
+static VALUE call_rb_str_vcatf(VALUE mesg, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
VALUE result = rb_str_vcatf(mesg, fmt, ap);
@@ -597,11 +567,24 @@ static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) {
return rb_str_unlocktmp(str);
}
+static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) {
+ rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc);
+ return rb_enc_interned_str_cstr(RSTRING_PTR(str), e);
+}
+
+static VALUE string_spec_rb_enc_interned_str(VALUE self, VALUE str, VALUE len, VALUE enc) {
+ rb_encoding *e = NIL_P(enc) ? 0 : rb_to_encoding(enc);
+ return rb_enc_interned_str(RSTRING_PTR(str), FIX2LONG(len), e);
+}
+
+static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) {
+ return rb_str_to_interned_str(str);
+}
+
void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3);
- rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1);
rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2);
rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2);
rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2);
@@ -617,6 +600,7 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_cat_cstr", string_spec_rb_str_cat_cstr, 2);
rb_define_method(cls, "rb_str_cat_cstr_constant", string_spec_rb_str_cat_cstr_constant, 1);
rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2);
+ rb_define_method(cls, "rb_str_strlen", string_spec_rb_str_strlen, 1);
rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3);
rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5);
rb_define_method(cls, "rb_str_drop_bytes", string_spec_rb_str_drop_bytes, 2);
@@ -642,10 +626,6 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_new3", string_spec_rb_str_new3, 1);
rb_define_method(cls, "rb_str_new4", string_spec_rb_str_new4, 1);
rb_define_method(cls, "rb_str_new5", string_spec_rb_str_new5, 3);
-#ifndef RUBY_VERSION_IS_3_2
- rb_define_method(cls, "rb_tainted_str_new", string_spec_rb_tainted_str_new, 2);
- rb_define_method(cls, "rb_tainted_str_new2", string_spec_rb_tainted_str_new2, 1);
-#endif
rb_define_method(cls, "rb_str_plus", string_spec_rb_str_plus, 2);
rb_define_method(cls, "rb_str_times", string_spec_rb_str_times, 2);
rb_define_method(cls, "rb_str_modify_expand", string_spec_rb_str_modify_expand, 2);
@@ -698,6 +678,9 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1);
rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1);
rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1);
+ rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2);
+ rb_define_method(cls, "rb_enc_interned_str", string_spec_rb_enc_interned_str, 3);
+ rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/struct_spec.c b/spec/ruby/optional/capi/ext/struct_spec.c
index 0393d6937d..756cfca8dd 100644
--- a/spec/ruby/optional/capi/ext/struct_spec.c
+++ b/spec/ruby/optional/capi/ext/struct_spec.c
@@ -15,13 +15,11 @@ static VALUE struct_spec_rb_struct_getmember(VALUE self, VALUE st, VALUE key) {
return rb_struct_getmember(st, SYM2ID(key));
}
-static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass)
-{
+static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass) {
return rb_ary_dup(rb_struct_s_members(klass));
}
-static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st)
-{
+static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st) {
return rb_ary_dup(rb_struct_members(st));
}
@@ -30,7 +28,7 @@ static VALUE struct_spec_rb_struct_aset(VALUE self, VALUE st, VALUE key, VALUE v
}
/* Only allow setting three attributes, should be sufficient for testing. */
-static VALUE struct_spec_struct_define(VALUE self, VALUE name,
+static VALUE struct_spec_rb_struct_define(VALUE self, VALUE name,
VALUE attr1, VALUE attr2, VALUE attr3) {
const char *a1 = StringValuePtr(attr1);
@@ -44,7 +42,7 @@ static VALUE struct_spec_struct_define(VALUE self, VALUE name,
}
/* Only allow setting three attributes, should be sufficient for testing. */
-static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer,
+static VALUE struct_spec_rb_struct_define_under(VALUE self, VALUE outer,
VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) {
const char *nm = StringValuePtr(name);
@@ -56,17 +54,35 @@ static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer,
}
static VALUE struct_spec_rb_struct_new(VALUE self, VALUE klass,
- VALUE a, VALUE b, VALUE c)
-{
-
+ VALUE a, VALUE b, VALUE c) {
return rb_struct_new(klass, a, b, c);
}
-static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st)
-{
+static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) {
return rb_struct_size(st);
}
+static VALUE struct_spec_rb_struct_initialize(VALUE self, VALUE st, VALUE values) {
+ return rb_struct_initialize(st, values);
+}
+
+#if defined(RUBY_VERSION_IS_3_3)
+/* Only allow setting three attributes, should be sufficient for testing. */
+static VALUE struct_spec_rb_data_define(VALUE self, VALUE superclass,
+ VALUE attr1, VALUE attr2, VALUE attr3) {
+
+ const char *a1 = StringValuePtr(attr1);
+ const char *a2 = StringValuePtr(attr2);
+ const char *a3 = StringValuePtr(attr3);
+
+ if (superclass == Qnil) {
+ superclass = 0;
+ }
+
+ return rb_data_define(superclass, a1, a2, a3, NULL);
+}
+#endif
+
void Init_struct_spec(void) {
VALUE cls = rb_define_class("CApiStructSpecs", rb_cObject);
rb_define_method(cls, "rb_struct_aref", struct_spec_rb_struct_aref, 2);
@@ -74,10 +90,14 @@ void Init_struct_spec(void) {
rb_define_method(cls, "rb_struct_s_members", struct_spec_rb_struct_s_members, 1);
rb_define_method(cls, "rb_struct_members", struct_spec_rb_struct_members, 1);
rb_define_method(cls, "rb_struct_aset", struct_spec_rb_struct_aset, 3);
- rb_define_method(cls, "rb_struct_define", struct_spec_struct_define, 4);
- rb_define_method(cls, "rb_struct_define_under", struct_spec_struct_define_under, 5);
+ rb_define_method(cls, "rb_struct_define", struct_spec_rb_struct_define, 4);
+ rb_define_method(cls, "rb_struct_define_under", struct_spec_rb_struct_define_under, 5);
rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4);
rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1);
+ rb_define_method(cls, "rb_struct_initialize", struct_spec_rb_struct_initialize, 2);
+#if defined(RUBY_VERSION_IS_3_3)
+ rb_define_method(cls, "rb_data_define", struct_spec_rb_data_define, 4);
+#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c
index be812d796f..ac77e4e813 100644
--- a/spec/ruby/optional/capi/ext/thread_spec.c
+++ b/spec/ruby/optional/capi/ext/thread_spec.c
@@ -8,7 +8,10 @@
#include <unistd.h>
#endif
#if defined(_WIN32)
-#define pipe(p) rb_w32_pipe(p)
+#include "ruby/win32.h"
+#define read rb_w32_read
+#define write rb_w32_write
+#define pipe rb_w32_pipe
#endif
#ifndef _WIN32
@@ -23,10 +26,6 @@ static VALUE thread_spec_rb_thread_alone(VALUE self) {
return rb_thread_alone() ? Qtrue : Qfalse;
}
-#if defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
/* This is unblocked by unblock_func(). */
static void* blocking_gvl_func(void* data) {
int rfd = *(int *)data;
@@ -68,7 +67,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl(VALUE self) {
}
/* This is unblocked by a signal. */
-static void* blocking_gvl_func_for_udf_io(void *data) {
+static void* blocking_gvl_func_for_ubf_io(void *data) {
int rfd = (int)(size_t)data;
char dummy;
@@ -88,7 +87,7 @@ static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) {
rb_raise(rb_eRuntimeError, "could not create pipe");
}
- ret = rb_thread_call_without_gvl(blocking_gvl_func_for_udf_io,
+ ret = rb_thread_call_without_gvl(blocking_gvl_func_for_ubf_io,
(void*)(size_t)fds[0], RUBY_UBF_IO, 0);
close(fds[0]);
close(fds[1]);
@@ -119,7 +118,6 @@ static VALUE thread_spec_rb_thread_wait_for(VALUE self, VALUE s, VALUE ms) {
return Qnil;
}
-
VALUE thread_spec_call_proc(void *arg_ptr) {
VALUE arg_array = (VALUE)arg_ptr;
VALUE arg = rb_ary_pop(arg_array);
@@ -168,6 +166,12 @@ static VALUE thread_spec_ruby_native_thread_p_new_thread(VALUE self) {
#endif
}
+#ifdef RUBY_VERSION_IS_4_0
+static VALUE thread_spec_ruby_thread_has_gvl_p(VALUE self) {
+ return ruby_thread_has_gvl_p() ? Qtrue : Qfalse;
+}
+#endif
+
void Init_thread_spec(void) {
VALUE cls = rb_define_class("CApiThreadSpecs", rb_cObject);
rb_define_method(cls, "rb_thread_alone", thread_spec_rb_thread_alone, 0);
@@ -181,6 +185,9 @@ void Init_thread_spec(void) {
rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2);
rb_define_method(cls, "ruby_native_thread_p", thread_spec_ruby_native_thread_p, 0);
rb_define_method(cls, "ruby_native_thread_p_new_thread", thread_spec_ruby_native_thread_p_new_thread, 0);
+#ifdef RUBY_VERSION_IS_4_0
+ rb_define_method(cls, "ruby_thread_has_gvl_p", thread_spec_ruby_thread_has_gvl_p, 0);
+#endif
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/tracepoint_spec.c b/spec/ruby/optional/capi/ext/tracepoint_spec.c
index 78c459d6cb..6666c8f85c 100644
--- a/spec/ruby/optional/capi/ext/tracepoint_spec.c
+++ b/spec/ruby/optional/capi/ext/tracepoint_spec.c
@@ -17,7 +17,7 @@ static VALUE tracepoint_spec_rb_tracepoint_new(VALUE self, VALUE data) {
return rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, callback, (void*) data);
}
-static VALUE tracepoint_spec_callback_called(VALUE self){
+static VALUE tracepoint_spec_callback_called(VALUE self) {
return callback_called;
}
diff --git a/spec/ruby/optional/capi/ext/typed_data_spec.c b/spec/ruby/optional/capi/ext/typed_data_spec.c
index eca2b667cc..221f1c8ac4 100644
--- a/spec/ruby/optional/capi/ext/typed_data_spec.c
+++ b/spec/ruby/optional/capi/ext/typed_data_spec.c
@@ -106,6 +106,14 @@ VALUE sws_typed_wrap_struct(VALUE self, VALUE val) {
return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar);
}
+#undef RUBY_UNTYPED_DATA_WARNING
+#define RUBY_UNTYPED_DATA_WARNING 0
+VALUE sws_untyped_wrap_struct(VALUE self, VALUE val) {
+ int* data = (int*) malloc(sizeof(int));
+ *data = FIX2INT(val);
+ return Data_Wrap_Struct(rb_cObject, NULL, free, data);
+}
+
VALUE sws_typed_get_struct(VALUE self, VALUE obj) {
struct sample_typed_wrapped_struct* bar;
TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar);
@@ -165,12 +173,17 @@ VALUE sws_typed_rb_check_typeddata_different_type(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_other_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
+VALUE sws_typed_RTYPEDDATA_P(VALUE self, VALUE obj) {
+ return RTYPEDDATA_P(obj) ? Qtrue : Qfalse;
+}
+
void Init_typed_data_spec(void) {
VALUE cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject);
rb_define_alloc_func(cls, sdaf_alloc_typed_func);
rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0);
cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject);
rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1);
+ rb_define_method(cls, "untyped_wrap_struct", sws_untyped_wrap_struct, 1);
rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1);
rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1);
rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1);
@@ -181,6 +194,7 @@ void Init_typed_data_spec(void) {
rb_define_method(cls, "rb_check_typeddata_same_type", sws_typed_rb_check_typeddata_same_type, 1);
rb_define_method(cls, "rb_check_typeddata_same_type_parent", sws_typed_rb_check_typeddata_same_type_parent, 1);
rb_define_method(cls, "rb_check_typeddata_different_type", sws_typed_rb_check_typeddata_different_type, 1);
+ rb_define_method(cls, "RTYPEDDATA_P", sws_typed_RTYPEDDATA_P, 1);
}
#ifdef __cplusplus
diff --git a/spec/ruby/optional/capi/ext/util_spec.c b/spec/ruby/optional/capi/ext/util_spec.c
index 95ba71ea9d..b5bde420d2 100644
--- a/spec/ruby/optional/capi/ext/util_spec.c
+++ b/spec/ruby/optional/capi/ext/util_spec.c
@@ -62,22 +62,17 @@ static VALUE util_spec_rb_get_kwargs(VALUE self, VALUE keyword_hash, VALUE keys,
int len = RARRAY_LENINT(keys);
int values_len = req + (opt < 0 ? -1 - opt : opt);
- int i = 0;
- ID *ids = (ID*) malloc(sizeof(VALUE) * len);
- VALUE *results = (VALUE*) malloc(sizeof(VALUE) * values_len);
- int extracted = 0;
- VALUE ary = Qundef;
+ ID *ids = (ID *)alloca(sizeof(VALUE) * len);
+ VALUE *results = (VALUE *)alloca(sizeof(VALUE) * values_len);
- for (i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++) {
ids[i] = SYM2ID(rb_ary_entry(keys, i));
}
- extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results);
- ary = rb_ary_new_from_values(extracted, results);
- free(results);
- free(ids);
- return ary;
+ int extracted = rb_get_kwargs(keyword_hash, ids, req, opt, results);
+
+ return rb_ary_new_from_values(extracted, results);
}
static VALUE util_spec_rb_long2int(VALUE self, VALUE n) {
diff --git a/spec/ruby/optional/capi/fiber_spec.rb b/spec/ruby/optional/capi/fiber_spec.rb
index 357033f860..05d867498c 100644
--- a/spec/ruby/optional/capi/fiber_spec.rb
+++ b/spec/ruby/optional/capi/fiber_spec.rb
@@ -1,5 +1,4 @@
require_relative 'spec_helper'
-require 'fiber'
load_extension('fiber')
@@ -49,41 +48,39 @@ describe "C-API Fiber function" do
end
end
- ruby_version_is '3.1' do
- describe "rb_fiber_raise" do
- it "raises an exception on the resumed fiber" do
- fiber = Fiber.new do
- begin
- Fiber.yield
- rescue => error
- error
- end
+ describe "rb_fiber_raise" do
+ it "raises an exception on the resumed fiber" do
+ fiber = Fiber.new do
+ begin
+ Fiber.yield
+ rescue => error
+ error
end
+ end
- fiber.resume
+ fiber.resume
- result = @s.rb_fiber_raise(fiber, "Boom!")
- result.should be_an_instance_of(RuntimeError)
- result.message.should == "Boom!"
- end
+ result = @s.rb_fiber_raise(fiber, "Boom!")
+ result.should be_an_instance_of(RuntimeError)
+ result.message.should == "Boom!"
+ end
- it "raises an exception on the transferred fiber" do
- main = Fiber.current
+ it "raises an exception on the transferred fiber" do
+ main = Fiber.current
- fiber = Fiber.new do
- begin
- main.transfer
- rescue => error
- error
- end
+ fiber = Fiber.new do
+ begin
+ main.transfer
+ rescue => error
+ error
end
+ end
- fiber.transfer
+ fiber.transfer
- result = @s.rb_fiber_raise(fiber, "Boom!")
- result.should be_an_instance_of(RuntimeError)
- result.message.should == "Boom!"
- end
+ result = @s.rb_fiber_raise(fiber, "Boom!")
+ result.should be_an_instance_of(RuntimeError)
+ result.message.should == "Boom!"
end
end
end
diff --git a/spec/ruby/optional/capi/file_spec.rb b/spec/ruby/optional/capi/file_spec.rb
index 96d731e4fa..12449b4e34 100644
--- a/spec/ruby/optional/capi/file_spec.rb
+++ b/spec/ruby/optional/capi/file_spec.rb
@@ -69,7 +69,7 @@ describe "C-API File function" do
end
it "does not call #to_str on a String" do
- obj = "path"
+ obj = +"path"
obj.should_not_receive(:to_str)
@s.FilePathValue(obj).should eql(obj)
end
diff --git a/spec/ruby/optional/capi/finalizer_spec.rb b/spec/ruby/optional/capi/finalizer_spec.rb
new file mode 100644
index 0000000000..162e8ea693
--- /dev/null
+++ b/spec/ruby/optional/capi/finalizer_spec.rb
@@ -0,0 +1,40 @@
+require_relative "spec_helper"
+
+extension_path = load_extension("finalizer")
+
+describe "CApiFinalizerSpecs" do
+ before :each do
+ @s = CApiFinalizerSpecs.new
+ end
+
+ describe "rb_define_finalizer" do
+ it "defines a finalizer on the object" do
+ code = <<~RUBY
+ require #{extension_path.dump}
+
+ obj = Object.new
+ finalizer = Proc.new { puts "finalizer run" }
+ CApiFinalizerSpecs.new.rb_define_finalizer(obj, finalizer)
+ RUBY
+
+ ruby_exe(code).should == "finalizer run\n"
+ end
+ end
+
+ describe "rb_undefine_finalizer" do
+ ruby_bug "#20981", "3.4.0"..."3.4.2" do
+ it "removes finalizers from the object" do
+ code = <<~RUBY
+ require #{extension_path.dump}
+
+ obj = Object.new
+ finalizer = Proc.new { puts "finalizer run" }
+ ObjectSpace.define_finalizer(obj, finalizer)
+ CApiFinalizerSpecs.new.rb_undefine_finalizer(obj)
+ RUBY
+
+ ruby_exe(code).should.empty?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/fixnum_spec.rb b/spec/ruby/optional/capi/fixnum_spec.rb
index aa02a0543b..e691aa3893 100644
--- a/spec/ruby/optional/capi/fixnum_spec.rb
+++ b/spec/ruby/optional/capi/fixnum_spec.rb
@@ -25,7 +25,7 @@ describe "CApiFixnumSpecs" do
end
end
- platform_is wordsize: 64 do # sizeof(long) > sizeof(int)
+ platform_is c_long_size: 64 do # sizeof(long) > sizeof(int)
it "raises a TypeError if passed nil" do
-> { @s.FIX2INT(nil) }.should raise_error(TypeError)
end
@@ -74,7 +74,7 @@ describe "CApiFixnumSpecs" do
end
end
- platform_is wordsize: 64 do # sizeof(long) > sizeof(int)
+ platform_is c_long_size: 64 do # sizeof(long) > sizeof(int)
it "raises a TypeError if passed nil" do
-> { @s.FIX2UINT(nil) }.should raise_error(TypeError)
end
diff --git a/spec/ruby/optional/capi/fixtures/kernel.rb b/spec/ruby/optional/capi/fixtures/kernel.rb
new file mode 100644
index 0000000000..d3fc7c57e8
--- /dev/null
+++ b/spec/ruby/optional/capi/fixtures/kernel.rb
@@ -0,0 +1,19 @@
+class CApiKernelSpecs
+ class ClassWithPublicMethod
+ def public_method(*, **)
+ :public
+ end
+ end
+
+ class ClassWithPrivateMethod
+ private def private_method(*, **)
+ :private
+ end
+ end
+
+ class ClassWithProtectedMethod
+ protected def protected_method(*, **)
+ :protected
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb
index d76ea7394f..d9661328ab 100644
--- a/spec/ruby/optional/capi/gc_spec.rb
+++ b/spec/ruby/optional/capi/gc_spec.rb
@@ -27,9 +27,36 @@ describe "CApiGCSpecs" do
end
describe "rb_global_variable" do
- it "keeps the value alive even if the value is assigned after rb_global_variable() is called" do
+ before :all do
GC.start
- @f.registered_before_rb_global_variable.should == "registered before rb_global_variable()"
+ end
+
+ describe "keeps the value alive even if the value is assigned after rb_global_variable() is called" do
+ it "for a string" do
+ @f.registered_before_rb_global_variable_string.should == "registered before rb_global_variable()"
+ end
+
+ it "for a bignum" do
+ @f.registered_before_rb_global_variable_bignum.should == 2**63 - 1
+ end
+
+ it "for a Float" do
+ @f.registered_before_rb_global_variable_float.should == 3.14
+ end
+ end
+
+ describe "keeps the value alive when the value is assigned before rb_global_variable() is called" do
+ it "for a string" do
+ @f.registered_after_rb_global_variable_string.should == "registered after rb_global_variable()"
+ end
+
+ it "for a bignum" do
+ @f.registered_after_rb_global_variable_bignum.should == 2**63 - 1
+ end
+
+ it "for a Float" do
+ @f.registered_after_rb_global_variable_float.should == 6.28
+ end
end
end
@@ -81,6 +108,10 @@ describe "CApiGCSpecs" do
it "can be called with an object" do
@f.rb_gc_register_mark_object(Object.new).should be_nil
end
+
+ it "keeps the value alive even if the value is not referenced by any Ruby object" do
+ @f.rb_gc_register_mark_object_not_referenced_float.should == 1.61
+ end
end
describe "rb_gc_latest_gc_info" do
diff --git a/spec/ruby/optional/capi/globals_spec.rb b/spec/ruby/optional/capi/globals_spec.rb
index 48677620bc..4657293e15 100644
--- a/spec/ruby/optional/capi/globals_spec.rb
+++ b/spec/ruby/optional/capi/globals_spec.rb
@@ -41,14 +41,19 @@ describe "CApiGlobalSpecs" do
@f.sb_get_global_value.should == "XYZ"
end
+ run = 0
+
it "rb_define_readonly_variable should define a new readonly global variable" do
+ name = "ro_gvar#{run += 1}"
+ eval <<~RUBY
# Check the gvar doesn't exist and ensure rb_gv_get doesn't implicitly declare the gvar,
# otherwise the rb_define_readonly_variable call will conflict.
- suppress_warning { @f.sb_gv_get("ro_gvar") } .should == nil
+ suppress_warning { @f.sb_gv_get("#{name}") }.should == nil
- @f.rb_define_readonly_variable("ro_gvar", 15)
- $ro_gvar.should == 15
- -> { $ro_gvar = 10 }.should raise_error(NameError)
+ @f.rb_define_readonly_variable("#{name}", 15)
+ $#{name}.should == 15
+ -> { $#{name} = 10 }.should raise_error(NameError)
+ RUBY
end
it "rb_define_hooked_variable should define a C hooked global variable" do
diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb
index a60467a66b..3a27de3bfa 100644
--- a/spec/ruby/optional/capi/hash_spec.rb
+++ b/spec/ruby/optional/capi/hash_spec.rb
@@ -50,6 +50,20 @@ describe "C-API Hash function" do
end
end
+ describe "rb_hash_new_capa" do
+ it "returns a new hash" do
+ @s.rb_hash_new_capa(3).should == {}
+ end
+
+ it "creates a hash with no default proc" do
+ @s.rb_hash_new_capa(3) {}.default_proc.should be_nil
+ end
+
+ it "raises RuntimeError when negative index is provided" do
+ -> { @s.rb_hash_new_capa(-1) }.should raise_error(RuntimeError, "st_table too big")
+ end
+ end
+
describe "rb_ident_hash_new" do
it "returns a new compare by identity hash" do
result = @s.rb_ident_hash_new
@@ -178,6 +192,61 @@ describe "C-API Hash function" do
end
end
+ describe "rb_hash_bulk_insert" do
+ it 'inserts key-value pairs into the hash' do
+ arr = [:a, 1, :b, 2, :c, 3]
+ hash = {}
+
+ @s.rb_hash_bulk_insert(arr.length, arr, hash)
+
+ hash.should == {a: 1, b: 2, c: 3}
+ end
+
+ it 'overwrites existing keys' do
+ arr = [:a, 4, :b, 5, :c, 6]
+ hash = {a: 1, b: 2}
+
+ @s.rb_hash_bulk_insert(arr.length, arr, hash)
+
+ hash.should == {a: 4, b: 5, c: 6}
+ end
+
+ it 'uses the last key in the array if it appears multiple times' do
+ arr = [:a, 1, :b, 2, :a, 3]
+ hash = {}
+
+ @s.rb_hash_bulk_insert(arr.length, arr, hash)
+
+ hash.should == {a: 3, b: 2}
+ end
+
+ it 'allows the array to be NULL if the length is zero' do
+ hash = {}
+
+ @s.rb_hash_bulk_insert(0, nil, hash)
+
+ hash.should == {}
+ end
+
+ it 'does not include any keys after the given length' do
+ arr = [:a, 1, :b, 2, :c, 3, :d, 4]
+ hash = {}
+
+ @s.rb_hash_bulk_insert(arr.length - 2, arr, hash)
+
+ hash.should == {a: 1, b: 2, c: 3}
+ end
+
+ it 'does not modify the hash if the length is zero' do
+ arr = []
+ hash = {a: 1, b: 2}
+
+ @s.rb_hash_bulk_insert(arr.length, arr, hash)
+
+ hash.should == {a: 1, b: 2}
+ end
+ end
+
describe "rb_hash_size" do
it "returns the size of the hash" do
hsh = {fast: 'car', good: 'music'}
diff --git a/spec/ruby/optional/capi/integer_spec.rb b/spec/ruby/optional/capi/integer_spec.rb
index e26735824e..f177374569 100644
--- a/spec/ruby/optional/capi/integer_spec.rb
+++ b/spec/ruby/optional/capi/integer_spec.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative 'spec_helper'
load_extension("integer")
@@ -140,6 +140,23 @@ describe "CApiIntegerSpecs" do
result.should == -1
@words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE"
end
+
+ it "converts numbers near the fixnum limit successfully" do
+ result = @s.rb_integer_pack(0x7123_4567_89ab_cdef, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x71"
+
+ result = @s.rb_integer_pack(2**62-1, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F"
+
+ result = @s.rb_integer_pack(2**63-1, @words, 1, 8, 0,
+ CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP)
+ result.should == 1
+ @words.should == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F"
+ end
end
end
end
diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb
index 95717351a6..ab7a7fc8f6 100644
--- a/spec/ruby/optional/capi/io_spec.rb
+++ b/spec/ruby/optional/capi/io_spec.rb
@@ -1,4 +1,5 @@
require_relative 'spec_helper'
+require_relative '../../fixtures/io'
load_extension('io')
@@ -175,13 +176,18 @@ describe "C-API IO function" do
end
end
- describe "GetOpenFile" do
+ describe "rb_io_descriptor or GetOpenFile" do
it "allows access to the system fileno" do
@o.GetOpenFile_fd($stdin).should == 0
@o.GetOpenFile_fd($stdout).should == 1
@o.GetOpenFile_fd($stderr).should == 2
@o.GetOpenFile_fd(@io).should == @io.fileno
end
+
+ it "raises IOError if the IO is closed" do
+ @io.close
+ -> { @o.GetOpenFile_fd(@io) }.should raise_error(IOError, "closed stream")
+ end
end
describe "rb_io_binmode" do
@@ -256,6 +262,41 @@ describe "C-API IO function" do
end
end
+ describe "rb_io_maybe_wait_writable" do
+ it "returns mask for events if operation was interrupted" do
+ @o.rb_io_maybe_wait_writable(Errno::EINTR::Errno, @w_io, nil).should == IO::WRITABLE
+ end
+
+ it "returns 0 if there is no error condition" do
+ @o.rb_io_maybe_wait_writable(0, @w_io, nil).should == 0
+ end
+
+ it "raises an IOError if the IO is closed" do
+ @w_io.close
+ -> { @o.rb_io_maybe_wait_writable(0, @w_io, nil) }.should raise_error(IOError, "closed stream")
+ end
+
+ it "raises an IOError if the IO is not initialized" do
+ -> { @o.rb_io_maybe_wait_writable(0, IO.allocate, nil) }.should raise_error(IOError, "uninitialized stream")
+ end
+
+ it "can be interrupted" do
+ IOSpec.exhaust_write_buffer(@w_io)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ t = Thread.new do
+ @o.rb_io_maybe_wait_writable(0, @w_io, 10)
+ end
+
+ Thread.pass until t.stop?
+ t.kill
+ t.join
+
+ finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ (finish - start).should < 9
+ end
+ end
+
describe "rb_thread_fd_writable" do
it "waits til an fd is ready for writing" do
@o.rb_thread_fd_writable(@w_io).should be_nil
@@ -305,6 +346,53 @@ describe "C-API IO function" do
thr.join
end
end
+
+ describe "rb_io_maybe_wait_readable" do
+ it "returns mask for events if operation was interrupted" do
+ @o.rb_io_maybe_wait_readable(Errno::EINTR::Errno, @r_io, nil, false).should == IO::READABLE
+ end
+
+ it "returns 0 if there is no error condition" do
+ @o.rb_io_maybe_wait_readable(0, @r_io, nil, false).should == 0
+ end
+
+ it "blocks until the io is readable and returns events that actually occurred" do
+ @o.instance_variable_set :@write_data, false
+ thr = Thread.new do
+ Thread.pass until @o.instance_variable_get(:@write_data)
+ @w_io.write "rb_io_wait_readable"
+ end
+
+ @o.rb_io_maybe_wait_readable(Errno::EAGAIN::Errno, @r_io, IO::READABLE, true).should == IO::READABLE
+ @o.instance_variable_get(:@read_data).should == "rb_io_wait_re"
+
+ thr.join
+ end
+
+ it "can be interrupted" do
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ t = Thread.new do
+ @o.rb_io_maybe_wait_readable(0, @r_io, 10, false)
+ end
+
+ Thread.pass until t.stop?
+ t.kill
+ t.join
+
+ finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ (finish - start).should < 9
+ end
+
+ it "raises an IOError if the IO is closed" do
+ @r_io.close
+ -> { @o.rb_io_maybe_wait_readable(0, @r_io, nil, false) }.should raise_error(IOError, "closed stream")
+ end
+
+ it "raises an IOError if the IO is not initialized" do
+ -> { @o.rb_io_maybe_wait_readable(0, IO.allocate, nil, false) }.should raise_error(IOError, "uninitialized stream")
+ end
+ end
end
describe "rb_thread_wait_fd" do
@@ -344,10 +432,242 @@ describe "C-API IO function" do
@o.rb_wait_for_single_fd(@r_io, 1, 0, 0).should == 0
end
end
+
+ describe "rb_io_maybe_wait" do
+ it "waits til an fd is ready for reading" do
+ start = false
+ thr = Thread.new do
+ start = true
+ sleep 0.05
+ @w_io.write "rb_io_maybe_wait"
+ end
+
+ Thread.pass until start
+
+ @o.rb_io_maybe_wait(Errno::EAGAIN::Errno, @r_io, IO::READABLE, nil).should == IO::READABLE
+
+ thr.join
+ end
+
+ it "returns mask for events if operation was interrupted" do
+ @o.rb_io_maybe_wait(Errno::EINTR::Errno, @w_io, IO::WRITABLE, nil).should == IO::WRITABLE
+ end
+
+ it "raises an IOError if the IO is closed" do
+ @w_io.close
+ -> { @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil) }.should raise_error(IOError, "closed stream")
+ end
+
+ it "raises an IOError if the IO is not initialized" do
+ -> { @o.rb_io_maybe_wait(0, IO.allocate, IO::WRITABLE, nil) }.should raise_error(IOError, "uninitialized stream")
+ end
+
+ it "can be interrupted when waiting for READABLE event" do
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ t = Thread.new do
+ @o.rb_io_maybe_wait(0, @r_io, IO::READABLE, 10)
+ end
+
+ Thread.pass until t.stop?
+ t.kill
+ t.join
+
+ finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ (finish - start).should < 9
+ end
+
+ it "can be interrupted when waiting for WRITABLE event" do
+ IOSpec.exhaust_write_buffer(@w_io)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ t = Thread.new do
+ @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, 10)
+ end
+
+ Thread.pass until t.stop?
+ t.kill
+ t.join
+
+ finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ (finish - start).should < 9
+ end
+ end
+
+ ruby_version_is "3.3" do
+ describe "rb_io_mode" do
+ it "returns the mode" do
+ (@o.rb_io_mode(@r_io) & 0b11).should == 0b01
+ (@o.rb_io_mode(@w_io) & 0b11).should == 0b10
+ (@o.rb_io_mode(@rw_io) & 0b11).should == 0b11
+ end
+ end
+
+ describe "rb_io_path" do
+ it "returns the IO#path" do
+ @o.rb_io_path(@r_io).should == @r_io.path
+ @o.rb_io_path(@rw_io).should == @rw_io.path
+ @o.rb_io_path(@rw_io).should == @name
+ end
+ end
+
+ describe "rb_io_closed_p" do
+ it "returns false when io is not closed" do
+ @o.rb_io_closed_p(@r_io).should == false
+ @r_io.closed?.should == false
+ end
+
+ it "returns true when io is closed" do
+ @r_io.close
+
+ @o.rb_io_closed_p(@r_io).should == true
+ @r_io.closed?.should == true
+ end
+ end
+
+ quarantine! do # "Errno::EBADF: Bad file descriptor" at closing @r_io, @rw_io etc in the after :each hook
+ describe "rb_io_open_descriptor" do
+ it "creates a new IO instance" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.should.is_a?(IO)
+ end
+
+ it "return an instance of the specified class" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.class.should == File
+
+ io = @o.rb_io_open_descriptor(IO, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.class.should == IO
+ end
+
+ it "sets the specified file descriptor" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.fileno.should == @r_io.fileno
+ end
+
+ it "sets the specified path" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.path.should == "a.txt"
+ end
+
+ it "sets the specified mode" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_BINMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.should.binmode?
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, CApiIOSpecs::FMODE_TEXTMODE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.should_not.binmode?
+ end
+
+ it "sets the specified timeout" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.timeout.should == 60
+ end
+
+ it "sets the specified internal encoding" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.internal_encoding.should == Encoding::US_ASCII
+ end
+
+ it "sets the specified external encoding" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.external_encoding.should == Encoding::UTF_8
+ end
+
+ it "does not apply the specified encoding flags" do
+ name = tmp("rb_io_open_descriptor_specs")
+ File.write(name, "123\r\n456\n89")
+ file = File.open(name, "r")
+
+ io = @o.rb_io_open_descriptor(File, file.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", CApiIOSpecs::ECONV_UNIVERSAL_NEWLINE_DECORATOR, {})
+ io.read_nonblock(20).should == "123\r\n456\n89"
+ ensure
+ file.close
+ rm_r name
+ end
+
+ it "ignores the IO open options" do
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {external_encoding: "windows-1251"})
+ io.external_encoding.should == Encoding::UTF_8
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {internal_encoding: "windows-1251"})
+ io.internal_encoding.should == Encoding::US_ASCII
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {encoding: "windows-1251:binary"})
+ io.external_encoding.should == Encoding::UTF_8
+ io.internal_encoding.should == Encoding::US_ASCII
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {textmode: false})
+ io.should_not.binmode?
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {binmode: true})
+ io.should_not.binmode?
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {autoclose: false})
+ io.should.autoclose?
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, "a.txt", 60, "US-ASCII", "UTF-8", 0, {path: "a.txt"})
+ io.path.should == "a.txt"
+ end
+
+ it "ignores the IO encoding options" do
+ io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_WRITABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {crlf_newline: true})
+
+ io.write("123\r\n456\n89")
+ io.flush
+
+ @r_io.read_nonblock(20).should == "123\r\n456\n89"
+ end
+
+ it "allows wrong mode" do
+ io = @o.rb_io_open_descriptor(File, @w_io.fileno, CApiIOSpecs::FMODE_READABLE, "a.txt", 60, "US-ASCII", "UTF-8", 0, {})
+ io.should.is_a?(File)
+
+ platform_is_not :windows do
+ -> { io.read_nonblock(1) }.should raise_error(Errno::EBADF)
+ end
+
+ platform_is :windows do
+ -> { io.read_nonblock(1) }.should raise_error(IO::EWOULDBLOCKWaitReadable)
+ end
+ end
+
+ it "tolerates NULL as rb_io_encoding *encoding parameter" do
+ io = @o.rb_io_open_descriptor_without_encoding(File, @r_io.fileno, 0, "a.txt", 60)
+ io.should.is_a?(File)
+ end
+
+ it "deduplicates path String" do
+ path = "a.txt".dup
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
+ io.path.should_not equal(path)
+
+ path = "a.txt".freeze
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
+ io.path.should_not equal(path)
+ end
+
+ it "calls #to_str to convert a path to a String" do
+ path = Object.new
+ def path.to_str; "a.txt"; end
+
+ io = @o.rb_io_open_descriptor(File, @r_io.fileno, 0, path, 60, "US-ASCII", "UTF-8", 0, {})
+
+ io.path.should == "a.txt"
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ describe "rb_io_maybe_wait" do
+ it "returns nil if there is no error condition" do
+ @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == nil
+ end
+ end
+ end
end
describe "rb_fd_fix_cloexec" do
-
before :each do
@o = CApiIOSpecs.new
diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb
index 54d8d8a8d3..6633ee50c1 100644
--- a/spec/ruby/optional/capi/kernel_spec.rb
+++ b/spec/ruby/optional/capi/kernel_spec.rb
@@ -1,12 +1,21 @@
require_relative 'spec_helper'
+require_relative 'fixtures/kernel'
kernel_path = load_extension("kernel")
+class CApiKernelSpecs::Exc < StandardError
+end
+exception_class = CApiKernelSpecs::Exc
+
describe "C-API Kernel function" do
before :each do
@s = CApiKernelSpecs.new
end
+ after :each do
+ @s.rb_errinfo.should == nil
+ end
+
describe "rb_block_given_p" do
it "returns false if no block is passed" do
@s.should_not.rb_block_given_p
@@ -77,6 +86,22 @@ describe "C-API Kernel function" do
-> { @s.rb_raise(h) }.should raise_error(TypeError)
h[:stage].should == :before
end
+
+ it "re-raises a rescued exception" do
+ -> do
+ begin
+ raise StandardError, "aaa"
+ rescue Exception
+ begin
+ @s.rb_raise({})
+ rescue TypeError
+ end
+
+ # should raise StandardError "aaa"
+ raise
+ end
+ end.should raise_error(StandardError, "aaa")
+ end
end
describe "rb_throw" do
@@ -131,26 +156,16 @@ describe "C-API Kernel function" do
end
describe "rb_warn" do
- before :each do
- @stderr, $stderr = $stderr, IOStub.new
- @verbose = $VERBOSE
- end
-
- after :each do
- $stderr = @stderr
- $VERBOSE = @verbose
- end
-
it "prints a message to $stderr if $VERBOSE evaluates to true" do
- $VERBOSE = true
- @s.rb_warn("This is a warning")
- $stderr.should =~ /This is a warning/
+ -> {
+ @s.rb_warn("This is a warning")
+ }.should complain(/warning: This is a warning/, verbose: true)
end
it "prints a message to $stderr if $VERBOSE evaluates to false" do
- $VERBOSE = false
- @s.rb_warn("This is a warning")
- $stderr.should =~ /This is a warning/
+ -> {
+ @s.rb_warn("This is a warning")
+ }.should complain(/warning: This is a warning/, verbose: false)
end
end
@@ -172,13 +187,35 @@ describe "C-API Kernel function" do
it "raises an exception from the given error" do
-> do
@s.rb_syserr_fail(Errno::EINVAL::Errno, "additional info")
- end.should raise_error(Errno::EINVAL, /additional info/)
+ end.should raise_error(Errno::EINVAL, "Invalid argument - additional info")
end
it "can take a NULL message" do
-> do
@s.rb_syserr_fail(Errno::EINVAL::Errno, nil)
- end.should raise_error(Errno::EINVAL)
+ end.should raise_error(Errno::EINVAL, "Invalid argument")
+ end
+
+ it "uses some kind of string as message when errno is unknown" do
+ -> { @s.rb_syserr_fail(-10, nil) }.should raise_error(SystemCallError, /[[:graph:]]+/)
+ end
+ end
+
+ describe "rb_syserr_fail_str" do
+ it "raises an exception from the given error" do
+ -> do
+ @s.rb_syserr_fail_str(Errno::EINVAL::Errno, "additional info")
+ end.should raise_error(Errno::EINVAL, "Invalid argument - additional info")
+ end
+
+ it "can take nil as a message" do
+ -> do
+ @s.rb_syserr_fail_str(Errno::EINVAL::Errno, nil)
+ end.should raise_error(Errno::EINVAL, "Invalid argument")
+ end
+
+ it "uses some kind of string as message when errno is unknown" do
+ -> { @s.rb_syserr_fail_str(-10, nil) }.should raise_error(SystemCallError, /[[:graph:]]+/)
end
end
@@ -205,10 +242,8 @@ describe "C-API Kernel function" do
@s.rb_yield(1) { break 73 }.should == 73
end
- platform_is_not :"solaris2.10" do # NOTE: i386-pc-solaris2.10
- it "rb_yield through a callback to a block that breaks with a value returns the value" do
- @s.rb_yield_indirected(1) { break 73 }.should == 73
- end
+ it "rb_yield through a callback to a block that breaks with a value returns the value" do
+ @s.rb_yield_indirected(1) { break 73 }.should == 73
end
it "rb_yield to block passed to enumerator" do
@@ -294,7 +329,7 @@ describe "C-API Kernel function" do
it "will allow cleanup code to run after a raise" do
proof = [] # Hold proof of work performed after the yield.
-> do
- @s.rb_protect_yield(77, proof) { |x| raise NameError}
+ @s.rb_protect_yield(77, proof) { |x| raise NameError }
end.should raise_error(NameError)
proof[0].should == 23
end
@@ -302,7 +337,7 @@ describe "C-API Kernel function" do
it "will return nil if an error was raised" do
proof = [] # Hold proof of work performed after the yield.
-> do
- @s.rb_protect_yield(77, proof) { |x| raise NameError}
+ @s.rb_protect_yield(77, proof) { |x| raise NameError }
end.should raise_error(NameError)
proof[0].should == 23
proof[1].should == nil
@@ -310,14 +345,21 @@ describe "C-API Kernel function" do
it "accepts NULL as status and returns nil if it failed" do
@s.rb_protect_null_status(42) { |x| x + 1 }.should == 43
- @s.rb_protect_null_status(42) { |x| raise }.should == nil
+ @s.rb_protect_null_status(42) { |x| raise NameError }.should == nil
+ @s.rb_errinfo().should.is_a? NameError
+ ensure
+ @s.rb_set_errinfo(nil)
end
- it "populates errinfo with the captured exception" do
+ it "populates rb_errinfo() with the captured exception" do
proof = []
- @s.rb_protect_errinfo(77, proof) { |x| raise NameError }.class.should == NameError
+ @s.rb_protect_ignore_status(77, proof) { |x| raise NameError }
+ @s.rb_errinfo().should.is_a? NameError
+ # Note: on CRuby $! is the NameError here, but not clear if that is desirable or bug
proof[0].should == 23
proof[1].should == nil
+ ensure
+ @s.rb_set_errinfo(nil)
end
end
@@ -381,9 +423,21 @@ describe "C-API Kernel function" do
-> { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError)
end
- it "makes $! available only during the 'rescue function' execution" do
- @s.rb_rescue(@std_error_proc, nil, -> *_ { $! }, nil).class.should == StandardError
+ it "sets $! and rb_errinfo() during the 'rescue function' execution" do
+ @s.rb_rescue(-> *_ { raise exception_class, '' }, nil, -> _, exc {
+ exc.should.is_a?(exception_class)
+ $!.should.equal?(exc)
+ @s.rb_errinfo.should.equal?(exc)
+ }, nil)
+
+ @s.rb_rescue(-> _ { @s.rb_raise({}) }, nil, -> _, exc {
+ exc.should.is_a?(TypeError)
+ $!.should.equal?(exc)
+ @s.rb_errinfo.should.equal?(exc)
+ }, nil)
+
$!.should == nil
+ @s.rb_errinfo.should == nil
end
it "returns the break value if the passed function yields to a block with a break" do
@@ -401,7 +455,7 @@ describe "C-API Kernel function" do
describe "rb_rescue2" do
it "only rescues if one of the passed exceptions is raised" do
- proc = -> x { x }
+ proc = -> x, _exc { x }
arg_error_proc = -> *_ { raise ArgumentError, '' }
run_error_proc = -> *_ { raise RuntimeError, '' }
type_error_proc = -> *_ { raise Exception, 'custom error' }
@@ -417,6 +471,23 @@ describe "C-API Kernel function" do
@s.rb_rescue2(-> *_ { raise RuntimeError, "foo" }, :no_exc, -> x { x }, :exc, Object.new, 42)
}.should raise_error(TypeError, /class or module required/)
end
+
+ it "sets $! and rb_errinfo() during the 'rescue function' execution" do
+ @s.rb_rescue2(-> *_ { raise exception_class, '' }, :no_exc, -> _, exc {
+ exc.should.is_a?(exception_class)
+ $!.should.equal?(exc)
+ @s.rb_errinfo.should.equal?(exc)
+ }, :exc, exception_class, ScriptError)
+
+ @s.rb_rescue2(-> *_ { @s.rb_raise({}) }, :no_exc, -> _, exc {
+ exc.should.is_a?(TypeError)
+ $!.should.equal?(exc)
+ @s.rb_errinfo.should.equal?(exc)
+ }, :exc, TypeError, ArgumentError)
+
+ $!.should == nil
+ @s.rb_errinfo.should == nil
+ end
end
describe "rb_catch" do
@@ -469,6 +540,40 @@ describe "C-API Kernel function" do
end
end
+ describe "rb_category_warn" do
+ it "emits a warning into stderr" do
+ Warning[:deprecated] = true
+
+ -> {
+ @s.rb_category_warn_deprecated
+ }.should complain(/warning: foo/, verbose: true)
+ end
+
+ it "supports printf format modifiers" do
+ Warning[:deprecated] = true
+
+ -> {
+ @s.rb_category_warn_deprecated_with_integer_extra_value(42)
+ }.should complain(/warning: foo 42/, verbose: true)
+ end
+
+ it "does not emits a warning when a category is disabled" do
+ Warning[:deprecated] = false
+
+ -> {
+ @s.rb_category_warn_deprecated
+ }.should_not complain(verbose: true)
+ end
+
+ it "does not emits a warning when $VERBOSE is nil" do
+ Warning[:deprecated] = true
+
+ -> {
+ @s.rb_category_warn_deprecated
+ }.should_not complain(verbose: nil)
+ end
+ end
+
describe "rb_ensure" do
it "executes passed function and returns its value" do
proc = -> x { x }
@@ -485,12 +590,33 @@ describe "C-API Kernel function" do
it "executes passed 'ensure function' when an exception is raised" do
foo = nil
- raise_proc = -> { raise '' }
+ raise_proc = -> _ { raise exception_class }
ensure_proc = -> x { foo = x }
- @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) rescue nil
+ -> {
+ @s.rb_ensure(raise_proc, nil, ensure_proc, :foo)
+ }.should raise_error(exception_class)
foo.should == :foo
end
+ it "sets $! and rb_errinfo() during the 'ensure function' execution" do
+ -> {
+ @s.rb_ensure(-> _ { raise exception_class }, nil, -> _ {
+ $!.should.is_a?(exception_class)
+ @s.rb_errinfo.should.is_a?(exception_class)
+ }, nil)
+ }.should raise_error(exception_class)
+
+ -> {
+ @s.rb_ensure(-> _ { @s.rb_raise({}) }, nil, -> _ {
+ $!.should.is_a?(TypeError)
+ @s.rb_errinfo.should.is_a?(TypeError)
+ }, nil)
+ }.should raise_error(TypeError)
+
+ $!.should == nil
+ @s.rb_errinfo.should == nil
+ end
+
it "raises the same exception raised inside passed function" do
raise_proc = -> *_ { raise RuntimeError, 'foo' }
proc = -> *_ { }
@@ -502,24 +628,31 @@ describe "C-API Kernel function" do
it "evaluates a string of ruby code" do
@s.rb_eval_string("1+1").should == 2
end
- end
- describe "rb_eval_cmd_kw" do
- it "evaluates a string of ruby code" do
- @s.rb_eval_cmd_kw("1+1", [], 0).should == 2
+ it "captures local variables when called within a method" do
+ a = 2
+ @s.rb_eval_string("a+1").should == 3
end
+ end
- it "calls a proc with the supplied arguments" do
- @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8]
- end
+ ruby_version_is ""..."4.0" do
+ describe "rb_eval_cmd_kw" do
+ it "evaluates a string of ruby code" do
+ @s.rb_eval_cmd_kw("1+1", [], 0).should == 2
+ end
+
+ it "calls a proc with the supplied arguments" do
+ @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8]
+ end
- it "calls a proc with keyword arguments if kw_splat is non zero" do
- a_proc = -> *x, **y {
- res = x.map { |i| i + 1 }
- y.each { |k, v| res << k; res << v }
- res
- }
- @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3]
+ it "calls a proc with keyword arguments if kw_splat is non zero" do
+ a_proc = -> *x, **y {
+ res = x.map { |i| i + 1 }
+ y.each { |k, v| res << k; res << v }
+ res
+ }
+ @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3]
+ end
end
end
@@ -597,47 +730,65 @@ describe "C-API Kernel function" do
@s.rb_funcallv(self, :empty, []).should == 42
@s.rb_funcallv(self, :sum, [1, 2]).should == 3
end
- end
- ruby_version_is "3.0" do
- describe "rb_funcallv_kw" do
- it "passes keyword arguments to the callee" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_funcallv(object, :private_method, []).should == :private
+ end
+
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_funcallv(object, :protected_method, []).should == :protected
+ end
+ end
- @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}]
- @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}]
- @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
- @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ describe "rb_funcallv_kw" do
+ it "passes keyword arguments to the callee" do
+ def m(*args, **kwargs)
+ [args, kwargs]
end
- it "raises TypeError if the last argument is not a Hash" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}]
+ @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}]
+ @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
+ @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ end
+
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_funcallv_kw(object, :private_method, [{}]).should == :private
+ end
+
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_funcallv_kw(object, :protected_method, [{}]).should == :protected
+ end
- -> {
- @s.rb_funcallv_kw(self, :m, [42])
- }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ it "raises TypeError if the last argument is not a Hash" do
+ def m(*args, **kwargs)
+ [args, kwargs]
end
+
+ -> {
+ @s.rb_funcallv_kw(self, :m, [42])
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
end
+ end
- describe "rb_keyword_given_p" do
- it "returns whether keywords were given to the C extension method" do
- h = {a: 1}
- empty = {}
- @s.rb_keyword_given_p(a: 1).should == true
- @s.rb_keyword_given_p("foo" => "bar").should == true
- @s.rb_keyword_given_p(**h).should == true
+ describe "rb_keyword_given_p" do
+ it "returns whether keywords were given to the C extension method" do
+ h = {a: 1}
+ empty = {}
+ @s.rb_keyword_given_p(a: 1).should == true
+ @s.rb_keyword_given_p("foo" => "bar").should == true
+ @s.rb_keyword_given_p(**h).should == true
- @s.rb_keyword_given_p(h).should == false
- @s.rb_keyword_given_p().should == false
- @s.rb_keyword_given_p(**empty).should == false
+ @s.rb_keyword_given_p(h).should == false
+ @s.rb_keyword_given_p().should == false
+ @s.rb_keyword_given_p(**empty).should == false
- @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true
- @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false
- end
+ @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true
+ @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false
end
end
@@ -676,21 +827,97 @@ describe "C-API Kernel function" do
end
describe 'rb_funcall_with_block' do
- before :each do
+ it "calls a method with block" do
@obj = Object.new
class << @obj
- def method_public; yield end
- def method_private; yield end
- private :method_private
+ def method_public(*args); [args, yield] end
end
+
+ @s.rb_funcall_with_block(@obj, :method_public, [1, 2], proc { :result }).should == [[1, 2], :result]
end
- it "calls a method with block" do
- @s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result
+ it "does not call a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+
+ -> {
+ @s.rb_funcall_with_block(object, :private_method, [], proc { })
+ }.should raise_error(NoMethodError, /private/)
+ end
+
+ it "does not call a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+
+ -> {
+ @s.rb_funcall_with_block(object, :protected_method, [], proc { })
+ }.should raise_error(NoMethodError, /protected/)
+ end
+ end
+
+ describe 'rb_funcall_with_block_kw' do
+ it "calls a method with keyword arguments and a block" do
+ @obj = Object.new
+ class << @obj
+ def method_public(*args, **kw, &block); [args, kw, block.call] end
+ end
+
+ @s.rb_funcall_with_block_kw(@obj, :method_public, [1, 2, {a: 2}], proc { :result }).should == [[1, 2], {a: 2}, :result]
end
it "does not call a private method" do
- -> { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/)
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+
+ -> {
+ @s.rb_funcall_with_block_kw(object, :private_method, [{}], proc { })
+ }.should raise_error(NoMethodError, /private/)
+ end
+
+ it "does not call a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+
+ -> {
+ @s.rb_funcall_with_block_kw(object, :protected_method, [{}], proc { })
+ }.should raise_error(NoMethodError, /protected/)
+ end
+ end
+
+ describe "rb_check_funcall" do
+ it "calls a method" do
+ @s.rb_check_funcall(1, :+, [2]).should == 3
+ end
+
+ it "returns Qundef if the method is not defined" do
+ obj = Object.new
+ @s.rb_check_funcall(obj, :foo, []).should == :Qundef
+ end
+
+ it "uses #respond_to? to check if the method is defined" do
+ ScratchPad.record []
+ obj = Object.new
+ def obj.respond_to?(name, priv)
+ ScratchPad << name
+ name == :foo || super
+ end
+ def obj.method_missing(name, *args)
+ name == :foo ? [name, 42] : super
+ end
+ @s.rb_check_funcall(obj, :foo, []).should == [:foo, 42]
+ ScratchPad.recorded.should == [:foo]
+ end
+
+ it "calls a private method" do
+ object = CApiKernelSpecs::ClassWithPrivateMethod.new
+ @s.rb_check_funcall(object, :private_method, []).should == :private
+ end
+
+ it "calls a protected method" do
+ object = CApiKernelSpecs::ClassWithProtectedMethod.new
+ @s.rb_check_funcall(object, :protected_method, []).should == :protected
+ end
+ end
+
+ describe "rb_str_format" do
+ it "returns a string according to format and arguments" do
+ @s.rb_str_format(3, [10, 2.5, "test"], "%d %f %s").should == "10 2.500000 test"
end
end
end
diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb
index d7c0ab9c52..af39ec0192 100644
--- a/spec/ruby/optional/capi/module_spec.rb
+++ b/spec/ruby/optional/capi/module_spec.rb
@@ -22,6 +22,8 @@ describe "CApiModule" do
it "sets a new constant on a module" do
@m.rb_const_set(CApiModuleSpecs::C, :W, 7)
CApiModuleSpecs::C::W.should == 7
+ ensure
+ CApiModuleSpecs::C.send(:remove_const, :W)
end
it "sets an existing constant's value" do
@@ -36,7 +38,7 @@ describe "CApiModule" do
CApiModuleSpecs::C.const_set(:_INVALID, 1)
}.should raise_error(NameError, /wrong constant name/)
- @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2)
+ suppress_warning { @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2) }
@m.rb_const_get(CApiModuleSpecs::C, :_INVALID).should == 2
# Ruby-level should still not allow access
@@ -93,6 +95,8 @@ describe "CApiModule" do
it "defines a new constant on a module" do
@m.rb_define_const(CApiModuleSpecs::C, "V", 7)
CApiModuleSpecs::C::V.should == 7
+ ensure
+ CApiModuleSpecs::C.send(:remove_const, :V)
end
it "sets an existing constant's value" do
diff --git a/spec/ruby/optional/capi/mutex_spec.rb b/spec/ruby/optional/capi/mutex_spec.rb
index 34659974f5..71a2212e36 100644
--- a/spec/ruby/optional/capi/mutex_spec.rb
+++ b/spec/ruby/optional/capi/mutex_spec.rb
@@ -85,5 +85,18 @@ describe "C-API Mutex functions" do
callback = -> { @m.locked?.should be_true }
@s.rb_mutex_synchronize(@m, callback)
end
+
+ it "returns a value returned from a callback" do
+ callback = -> { :foo }
+ @s.rb_mutex_synchronize(@m, callback).should == :foo
+ end
+
+ it "calls a C-function that accepts and returns non-VALUE values" do
+ @s.rb_mutex_synchronize_with_naughty_callback(@m).should == 42
+ end
+
+ it "calls a native function" do
+ @s.rb_mutex_synchronize_with_native_callback(@m, 42).should == 42
+ end
end
end
diff --git a/spec/ruby/optional/capi/numeric_spec.rb b/spec/ruby/optional/capi/numeric_spec.rb
index 95213d3f2b..e9667da5ba 100644
--- a/spec/ruby/optional/capi/numeric_spec.rb
+++ b/spec/ruby/optional/capi/numeric_spec.rb
@@ -106,7 +106,7 @@ describe "CApiNumericSpecs" do
@s.NUM2LONG(5).should == 5
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
it "converts -1 to an signed number" do
@s.NUM2LONG(-1).should == -1
end
@@ -120,7 +120,7 @@ describe "CApiNumericSpecs" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "converts -1 to an signed number" do
@s.NUM2LONG(-1).should == -1
end
@@ -210,7 +210,7 @@ describe "CApiNumericSpecs" do
@s.NUM2ULONG(5).should == 5
end
- platform_is wordsize: 32 do
+ platform_is c_long_size: 32 do
it "converts -1 to an unsigned number" do
@s.NUM2ULONG(-1).should == 4294967295
end
@@ -231,7 +231,7 @@ describe "CApiNumericSpecs" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
it "converts -1 to an unsigned number" do
@s.NUM2ULONG(-1).should == 18446744073709551615
end
diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb
index 9efc892202..8b4d8a9bba 100644
--- a/spec/ruby/optional/capi/object_spec.rb
+++ b/spec/ruby/optional/capi/object_spec.rb
@@ -219,7 +219,7 @@ describe "CApiObject" do
end
it "requires a ruby file" do
- $:.unshift File.dirname(__FILE__)
+ $:.unshift __dir__
@o.rb_require()
$foo.should == 7
end
@@ -686,11 +686,21 @@ describe "CApiObject" do
end
it "returns false if object passed to it is not frozen" do
- obj = ""
+ obj = +""
@o.rb_obj_frozen_p(obj).should == false
end
end
+ describe "redefining frozen? works" do
+ it "allows an object to override frozen?" do
+ obj = CApiObjectRedefinitionSpecs.new
+
+ obj.frozen?.should == false
+ obj.freeze
+ obj.frozen?.should == true
+ end
+ end
+
describe "rb_obj_taint" do
end
@@ -700,7 +710,7 @@ describe "CApiObject" do
end
it "does nothing when object isn't frozen" do
- obj = ""
+ obj = +""
-> { @o.rb_check_frozen(obj) }.should_not raise_error(TypeError)
end
end
@@ -888,15 +898,15 @@ describe "CApiObject" do
end
end
- # The `generic_iv_tbl` table and `*_generic_ivar` functions are for mutable
+ # The `generic_fields_tbl` table and `*_generic_ivar` functions are for mutable
# objects which do not store ivars directly in MRI such as RString, because
# there is no member iv_index_tbl (ivar table) such as in RObject and RClass.
describe "rb_copy_generic_ivar for objects which do not store ivars directly" do
it "copies the instance variables from one object to another" do
- original = "abc"
+ original = +"abc"
original.instance_variable_set(:@foo, :bar)
- clone = "def"
+ clone = +"def"
@o.rb_copy_generic_ivar(clone, original)
clone.instance_variable_get(:@foo).should == :bar
end
@@ -904,7 +914,7 @@ describe "CApiObject" do
describe "rb_free_generic_ivar for objects which do not store ivars directly" do
it "removes the instance variables from an object" do
- o = "abc"
+ o = +"abc"
o.instance_variable_set(:@baz, :flibble)
@o.rb_free_generic_ivar(o)
o.instance_variables.should == []
diff --git a/spec/ruby/optional/capi/proc_spec.rb b/spec/ruby/optional/capi/proc_spec.rb
index c839665ae9..8b94432f3e 100644
--- a/spec/ruby/optional/capi/proc_spec.rb
+++ b/spec/ruby/optional/capi/proc_spec.rb
@@ -82,6 +82,59 @@ describe "C-API Proc function" do
end
end
+ describe "rb_proc_call_kw" do
+ it "passes keyword arguments to the proc" do
+ prc = proc { |*args, **kw| [args, kw] }
+
+ @p.rb_proc_call_kw(prc, [{}]).should == [[], {}]
+ @p.rb_proc_call_kw(prc, [{a: 1}]).should == [[], {a: 1}]
+ @p.rb_proc_call_kw(prc, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
+ @p.rb_proc_call_kw(prc, [{b: 2}, {}]).should == [[{b: 2}], {}]
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @p.rb_proc_call_kw(proc {}, [42])
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ end
+ end
+
+ describe "rb_proc_call_with_block" do
+ it "calls the Proc and passes arguments and a block" do
+ prc = Proc.new { |a, b, &block| block.call(a * b) }
+ @p.rb_proc_call_with_block(prc, [6, 7], proc { |n| n * 2 }).should == 6 * 7 * 2
+ end
+
+ it "calls the Proc and passes arguments when a block is nil" do
+ prc = Proc.new { |a, b| a * b }
+ @p.rb_proc_call_with_block(prc, [6, 7], nil).should == 6 * 7
+ end
+ end
+
+ describe "rb_proc_call_with_block_kw" do
+ it "passes keyword arguments and a block to the proc" do
+ prc = proc { |*args, **kw, &block| [args, kw, block.call(42)] }
+ block = proc { |n| n }
+
+ @p.rb_proc_call_with_block_kw(prc, [{}], block).should == [[], {}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{a: 1}], block).should == [[], {a: 1}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {a: 1}], block).should == [[{b: 2}], {a: 1}, 42]
+ @p.rb_proc_call_with_block_kw(prc, [{b: 2}, {}], block).should == [[{b: 2}], {}, 42]
+ end
+
+ it "raises TypeError if the last argument is not a Hash" do
+ -> {
+ @p.rb_proc_call_with_block_kw(proc {}, [42], proc {})
+ }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
+ end
+
+ it "passes keyword arguments to the proc when a block is nil" do
+ prc = proc { |*args, **kw| [args, kw] }
+
+ @p.rb_proc_call_with_block_kw(prc, [{}], nil).should == [[], {}]
+ end
+ end
+
describe "rb_obj_is_proc" do
it "returns true for Proc" do
prc = Proc.new {|a,b| a * b }
diff --git a/spec/ruby/optional/capi/range_spec.rb b/spec/ruby/optional/capi/range_spec.rb
index 7a52dc7ff8..80c052e79a 100644
--- a/spec/ruby/optional/capi/range_spec.rb
+++ b/spec/ruby/optional/capi/range_spec.rb
@@ -69,7 +69,7 @@ describe "C-API Range function" do
describe "rb_range_beg_len" do
it "returns correct begin, length and result" do
r = 2..5
- begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 10, 0)
+ begp, lenp, result = @s.rb_range_beg_len(r, 10, 0)
result.should be_true
begp.should == 2
lenp.should == 4
@@ -77,19 +77,155 @@ describe "C-API Range function" do
it "returns nil when not in range" do
r = 2..5
- begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0)
+ begp, lenp, result = @s.rb_range_beg_len(r, 1, 0)
result.should be_nil
end
it "raises a RangeError when not in range and err is 1" do
r = -5..-1
- -> { @s.rb_range_beg_len(r, 0, 0, 1, 1) }.should raise_error(RangeError)
+ -> { @s.rb_range_beg_len(r, 1, 1) }.should raise_error(RangeError)
end
it "returns nil when not in range and err is 0" do
r = -5..-1
- begp, lenp, result = @s.rb_range_beg_len(r, 0, 0, 1, 0)
+ begp, lenp, result = @s.rb_range_beg_len(r, 1, 0)
result.should be_nil
end
end
+
+ describe "rb_arithmetic_sequence_extract" do
+ it "returns begin, end, step, exclude end of an instance of an Enumerator::ArithmeticSequence" do
+ enum = (10..20).step(5)
+ enum.should.kind_of?(Enumerator::ArithmeticSequence)
+
+ @s.rb_arithmetic_sequence_extract(enum).should == [1, 10, 20, 5, false]
+ end
+
+ it "returns begin, end, step, exclude end of an instance of a Range" do
+ range = (10..20)
+ @s.rb_arithmetic_sequence_extract(range).should == [1, 10, 20, 1, false]
+ end
+
+ it "returns begin, end, step, exclude end of a non-Range object with Range properties" do
+ object = Object.new
+ def object.begin
+ 10
+ end
+ def object.end
+ 20
+ end
+ def object.exclude_end?
+ false
+ end
+
+ @s.rb_arithmetic_sequence_extract(object).should == [1, 10, 20, 1, false]
+ end
+
+ it "returns failed status if given object is not Enumerator::ArithmeticSequence or Range or Range-like object" do
+ object = Object.new
+ @s.rb_arithmetic_sequence_extract(object).should == [0]
+ end
+ end
+
+ describe "rb_arithmetic_sequence_beg_len_step" do
+ it "returns correct begin, length, step and result" do
+ as = (2..5).step(5)
+ error_code = 0
+
+ success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+
+ beg.should == 2
+ len.should == 4
+ step.should == 5
+ end
+
+ it "takes into account excluded end boundary" do
+ as = (2...5).step(1)
+ error_code = 0
+
+ success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+ len.should == 3
+ end
+
+ it "adds length to negative begin boundary" do
+ as = (-2..5).step(1)
+ error_code = 0
+
+ success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+
+ beg.should == 4
+ len.should == 2
+ end
+
+ it "adds length to negative end boundary" do
+ as = (2..-1).step(1)
+ error_code = 0
+
+ success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+
+ beg.should == 2
+ len.should == 4
+ end
+
+ it "truncates arithmetic sequence length if end boundary greater than specified length value" do
+ as = (2..10).step(1)
+ error_code = 0
+
+ success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+ len.should == 4
+ end
+
+ it "returns inverted begin and end boundaries when step is negative" do
+ as = (2..5).step(-2)
+ error_code = 0
+
+ success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
+ success.should be_true
+
+ beg.should == 5
+ len.should == 0
+ step.should == -2
+ end
+
+ it "returns nil when not in range and error code = 0" do
+ as = (2..5).step(1)
+ error_code = 0
+
+ success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
+ success.should be_nil
+ end
+
+ it "returns nil when not in range, negative boundaries and error code = 0" do
+ as = (-5..-1).step(1)
+ error_code = 0
+
+ success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, 0)
+ success.should be_nil
+ end
+
+ it "returns begin, length and step and doesn't raise a RangeError when not in range and error code = 1" do
+ as = (2..5).step(1)
+ error_code = 1
+
+ success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
+ success.should be_true
+
+ beg.should == 2
+ len.should == 4
+ step.should == 1
+ end
+
+ it "returns nil and doesn't raise a RangeError when not in range, negative boundaries and error code = 1" do
+ as = (-5..-1).step(1)
+ error_code = 1
+
+ success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
+ success.should be_nil
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/rbasic_spec.rb b/spec/ruby/optional/capi/rbasic_spec.rb
index 577f2060da..7b5b5b2fed 100644
--- a/spec/ruby/optional/capi/rbasic_spec.rb
+++ b/spec/ruby/optional/capi/rbasic_spec.rb
@@ -1,7 +1,9 @@
require_relative 'spec_helper'
require_relative 'shared/rbasic'
load_extension("rbasic")
-load_extension("data")
+ruby_version_is ""..."3.4" do
+ load_extension("data")
+end
load_extension("array")
describe "RBasic support for regular objects" do
@@ -12,31 +14,35 @@ describe "RBasic support for regular objects" do
it_should_behave_like :rbasic
end
-describe "RBasic support for RData" do
- before :all do
- @specs = CApiRBasicRDataSpecs.new
- @wrapping = CApiWrappedStructSpecs.new
- @data = -> { [@wrapping.wrap_struct(1024), @wrapping.wrap_struct(1025)] }
- end
- it_should_behave_like :rbasic
+ruby_version_is ""..."3.4" do
+ describe "RBasic support for RData" do
+ before :all do
+ @specs = CApiRBasicRDataSpecs.new
+ @wrapping = CApiWrappedStructSpecs.new
+ @data = -> { [@wrapping.wrap_struct(1024), @wrapping.wrap_struct(1025)] }
+ end
+ it_should_behave_like :rbasic
- it "supports user flags" do
- obj, _ = @data.call
- initial = @specs.get_flags(obj)
- @specs.set_flags(obj, 1 << 14 | 1 << 16 | initial).should == 1 << 14 | 1 << 16 | initial
- @specs.get_flags(obj).should == 1 << 14 | 1 << 16 | initial
- @specs.set_flags(obj, initial).should == initial
- end
+ it "supports user flags" do
+ obj, _ = @data.call
+ initial = @specs.get_flags(obj)
+ @specs.set_flags(obj, 1 << 14 | 1 << 16 | initial).should == 1 << 14 | 1 << 16 | initial
+ @specs.get_flags(obj).should == 1 << 14 | 1 << 16 | initial
+ @specs.set_flags(obj, initial).should == initial
+ end
+
+ it "supports copying the flags from one object over to the other" do
+ obj1, obj2 = @data.call
+ initial = @specs.get_flags(obj1)
+ @specs.get_flags(obj2).should == initial
+ @specs.set_flags(obj1, 1 << 14 | 1 << 16 | initial)
+ @specs.get_flags(obj1).should == 1 << 14 | 1 << 16 | initial
- it "supports copying the flags from one object over to the other" do
- obj1, obj2 = @data.call
- initial = @specs.get_flags(obj1)
- @specs.get_flags(obj2).should == initial
- @specs.set_flags(obj1, 1 << 14 | 1 << 16 | initial)
- @specs.copy_flags(obj2, obj1)
- @specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial
- @specs.set_flags(obj1, initial)
- @specs.copy_flags(obj2, obj1)
- @specs.get_flags(obj2).should == initial
+ @specs.copy_flags(obj2, obj1)
+ @specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial
+ @specs.set_flags(obj1, initial)
+ @specs.copy_flags(obj2, obj1)
+ @specs.get_flags(obj2).should == initial
+ end
end
end
diff --git a/spec/ruby/optional/capi/regexp_spec.rb b/spec/ruby/optional/capi/regexp_spec.rb
index af366e17a2..49ac79f5c4 100644
--- a/spec/ruby/optional/capi/regexp_spec.rb
+++ b/spec/ruby/optional/capi/regexp_spec.rb
@@ -77,7 +77,7 @@ describe "C-API Regexp function" do
end
it "returns MatchData when used with rb_reg_match" do
- @p.rb_reg_match_backref_get(/a/, 'ab')[0].should == 'a'
+ @p.rb_reg_match_backref_get(/a/, 'ab')[0].should == 'a'
end
end
@@ -110,7 +110,7 @@ describe "C-API Regexp function" do
end
end
- describe "rb_memicmp" do
+ describe "rb_memcicmp" do
it "returns 0 for identical strings" do
@p.rb_memcicmp('Hello', 'Hello').should == 0
end
diff --git a/spec/ruby/optional/capi/set_spec.rb b/spec/ruby/optional/capi/set_spec.rb
new file mode 100644
index 0000000000..3e35be0505
--- /dev/null
+++ b/spec/ruby/optional/capi/set_spec.rb
@@ -0,0 +1,96 @@
+require_relative 'spec_helper'
+
+ruby_version_is "4.0" do
+ load_extension("set")
+
+ describe "C-API Set function" do
+ before :each do
+ @s = CApiSetSpecs.new
+ end
+
+ describe "rb_set_foreach" do
+ it "calls function with each element and arg" do
+ a = []
+ @s.rb_set_foreach(Set[1, 2], 3) {|*args| a.concat(args) }
+ a.should == [1, 3, 2, 3]
+ end
+
+ it "respects function return value" do
+ a = []
+ @s.rb_set_foreach(Set[1, 2], 3) do |*args|
+ a.concat(args)
+ false
+ end
+ a.should == [1, 3]
+ end
+ end
+
+ describe "rb_set_new" do
+ it "returns a new set" do
+ @s.rb_set_new.should == Set[]
+ end
+ end
+
+ describe "rb_set_new_capa" do
+ it "returns a new set" do
+ @s.rb_set_new_capa(3).should == Set[]
+ end
+ end
+
+ describe "rb_set_lookup" do
+ it "returns whether the element is in the set" do
+ set = Set[1]
+ @s.rb_set_lookup(set, 1).should == true
+ @s.rb_set_lookup(set, 2).should == false
+ end
+ end
+
+ describe "rb_set_add" do
+ it "adds element to set" do
+ set = Set[]
+ @s.rb_set_add(set, 1).should == true
+ set.should == Set[1]
+ @s.rb_set_add(set, 2).should == true
+ set.should == Set[1, 2]
+ end
+
+ it "returns false if element is already in set" do
+ set = Set[1]
+ @s.rb_set_add(set, 1).should == false
+ set.should == Set[1]
+ end
+ end
+
+ describe "rb_set_clear" do
+ it "empties and returns self" do
+ set = Set[1]
+ @s.rb_set_clear(set).should equal(set)
+ set.should == Set[]
+ end
+ end
+
+ describe "rb_set_delete" do
+ it "removes element from set" do
+ set = Set[1, 2]
+ @s.rb_set_delete(set, 1).should == true
+ set.should == Set[2]
+ @s.rb_set_delete(set, 2).should == true
+ set.should == Set[]
+ end
+
+ it "returns false if element is not already in set" do
+ set = Set[2]
+ @s.rb_set_delete(set, 1).should == false
+ set.should == Set[2]
+ end
+ end
+
+ describe "rb_set_size" do
+ it "returns number of elements in set" do
+ @s.rb_set_size(Set[]).should == 0
+ @s.rb_set_size(Set[1]).should == 1
+ @s.rb_set_size(Set[1,2]).should == 2
+ end
+ end
+ end
+end
diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb
index 95c3137143..e3485d4b7b 100644
--- a/spec/ruby/optional/capi/shared/rbasic.rb
+++ b/spec/ruby/optional/capi/shared/rbasic.rb
@@ -1,8 +1,6 @@
describe :rbasic, shared: true do
-
before :all do
specs = CApiRBasicSpecs.new
- @taint = ruby_version_is(''...'3.1') ? specs.taint_flag : 0
@freeze = specs.freeze_flag
end
diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb
index 2691aa1332..e7abf46e6c 100644
--- a/spec/ruby/optional/capi/spec_helper.rb
+++ b/spec/ruby/optional/capi/spec_helper.rb
@@ -74,12 +74,19 @@ def compile_extension(name)
init_mkmf unless required
create_makefile(ext, tmpdir)
else
+ # Workaround for digest C-API specs to find the ruby/digest.h header
+ # when run in the CRuby repository via make test-spec
+ if MSpecScript.instance_variable_defined?(:@testing_ruby)
+ ruby_repository_extra_include_dir = "-I#{RbConfig::CONFIG.fetch("prefix")}/#{RbConfig::CONFIG.fetch("EXTOUT")}/include"
+ end
+
File.write("extconf.rb", <<-RUBY)
require 'mkmf'
$ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')
# MRI magic to consider building non-bundled extensions
$extout = nil
append_cflags '-Wno-declaration-after-statement'
+ #{"append_cflags #{ruby_repository_extra_include_dir.inspect}" if ruby_repository_extra_include_dir}
create_makefile(#{ext.inspect})
RUBY
output = ruby_exe("extconf.rb")
@@ -115,13 +122,9 @@ def setup_make
opts = {}
if /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ make_flags
- begin
- r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
- w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
- rescue Errno::EBADF
- else
- opts[r] = r
- opts[w] = w
+ [$1, $2].each do |fd|
+ fd = fd.to_i(10)
+ opts[fd] = fd
end
end
diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb
index 3a47fa9762..72f20ee6a5 100644
--- a/spec/ruby/optional/capi/string_spec.rb
+++ b/spec/ruby/optional/capi/string_spec.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+# frozen_string_literal: false
require_relative 'spec_helper'
require_relative '../../shared/string/times'
@@ -47,7 +48,7 @@ describe "C-API String function" do
[Encoding::BINARY, Encoding::UTF_8].each do |enc|
describe "rb_str_set_len on a #{enc.name} String" do
before :each do
- @str = "abcdefghij".force_encoding(enc)
+ @str = "abcdefghij".dup.force_encoding(enc)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -99,7 +100,7 @@ describe "C-API String function" do
describe "rb_str_set_len on a UTF-16 String" do
before :each do
- @str = "abcdefghij".force_encoding(Encoding::UTF_16BE)
+ @str = "abcdefghij".dup.force_encoding(Encoding::UTF_16BE)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -112,7 +113,7 @@ describe "C-API String function" do
describe "rb_str_set_len on a UTF-32 String" do
before :each do
- @str = "abcdefghijkl".force_encoding(Encoding::UTF_32BE)
+ @str = "abcdefghijkl".dup.force_encoding(Encoding::UTF_32BE)
# Make sure to unshare the string
@s.rb_str_modify(@str)
end
@@ -190,11 +191,19 @@ describe "C-API String function" do
end
it "returns a new String object filled with \\0 bytes" do
- s = @s.rb_str_tmp_new(4)
- s.encoding.should == Encoding::BINARY
- s.bytesize.should == 4
- s.size.should == 4
- s.should == "\x00\x00\x00\x00"
+ lens = [4]
+
+ ruby_version_is "4.0" do
+ lens << 100
+ end
+
+ lens.each do |len|
+ s = @s.rb_str_tmp_new(len)
+ s.encoding.should == Encoding::BINARY
+ s.bytesize.should == len
+ s.size.should == len
+ s.should == "\x00" * len
+ end
end
end
@@ -231,7 +240,7 @@ describe "C-API String function" do
describe "rb_usascii_str_new" do
it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
result = @s.rb_usascii_str_new("abcdef", 3)
result.should == str
result.encoding.should == Encoding::US_ASCII
@@ -247,14 +256,14 @@ describe "C-API String function" do
it "returns US-ASCII string for non-US-ASCII string literal" do
str = @s.rb_usascii_str_new_lit_non_ascii
- str.should == "r\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::US_ASCII)
+ str.should == "r\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::US_ASCII)
str.encoding.should == Encoding::US_ASCII
end
end
describe "rb_usascii_str_new_cstr" do
it "creates a new String with US-ASCII Encoding" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
result = @s.rb_usascii_str_new_cstr("abc")
result.should == str
result.encoding.should == Encoding::US_ASCII
@@ -418,7 +427,7 @@ describe "C-API String function" do
describe "rb_enc_str_buf_cat" do
it "concatenates a C string literal to a ruby string with the given encoding" do
- input = "hello ".force_encoding(Encoding::US_ASCII)
+ input = "hello ".dup.force_encoding(Encoding::US_ASCII)
result = @s.rb_enc_str_buf_cat(input, "résumé", Encoding::UTF_8)
result.should == "hello résumé"
result.encoding.should == Encoding::UTF_8
@@ -448,6 +457,20 @@ describe "C-API String function" do
end
end
+ describe "rb_str_strlen" do
+ it 'returns 0 as the length of an empty string' do
+ @s.rb_str_strlen('').should == 0
+ end
+
+ it 'returns the number of characters in a string' do
+ @s.rb_str_strlen('hello').should == 5
+ end
+
+ it 'returns the number of characters in a string with multi-byte characters' do
+ @s.rb_str_strlen('こんにちは').should == 5
+ end
+ end
+
describe "rb_str_split" do
it "splits strings over a splitter" do
@s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"]
@@ -498,29 +521,10 @@ describe "C-API String function" do
end
end
- describe "rb_fstring" do
- it 'returns self if the String is frozen' do
- input = 'foo'.freeze
- output = @s.rb_fstring(input)
-
- output.should equal(input)
- output.should.frozen?
- end
-
- it 'returns a frozen copy if the String is not frozen' do
- input = 'foo'
- output = @s.rb_fstring(input)
-
- output.should.frozen?
- output.should_not equal(input)
- output.should == 'foo'
- end
- end
-
describe "rb_str_subseq" do
it "returns a byte-indexed substring" do
- str = "\x00\x01\x02\x03\x04".force_encoding("binary")
- @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary")
+ str = "\x00\x01\x02\x03\x04".dup.force_encoding("binary")
+ @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".dup.force_encoding("binary")
end
end
@@ -731,7 +735,7 @@ describe "C-API String function" do
end
it "increases the size of the string" do
- expected = "test".force_encoding("US-ASCII")
+ expected = "test".dup.force_encoding("US-ASCII")
str = @s.rb_str_resize(expected.dup, 12)
str.size.should == 12
str.bytesize.should == 12
@@ -862,11 +866,11 @@ describe "C-API String function" do
# it "transcodes a String to Encoding.default_internal if it is set" do
# Encoding.default_internal = Encoding::EUC_JP
#
-# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8")
+# - a = "\xE3\x81\x82\xe3\x82\x8c".dup.force_encoding("utf-8")
# + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8")
# s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8)
# -
-# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp")
+# - s.should == "\xA4\xA2\xA4\xEC".dup.force_encoding("euc-jp")
# + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('binary')
# + s.should == x
# s.encoding.should equal(Encoding::EUC_JP)
@@ -886,7 +890,7 @@ describe "C-API String function" do
describe "rb_locale_str_new" do
it "returns a String with 'locale' encoding" do
s = @s.rb_locale_str_new("abc", 3)
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
@@ -894,33 +898,37 @@ describe "C-API String function" do
describe "rb_locale_str_new_cstr" do
it "returns a String with 'locale' encoding" do
s = @s.rb_locale_str_new_cstr("abc")
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
describe "rb_str_conv_enc" do
it "returns the original String when to encoding is not specified" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a)
end
it "returns the original String if a transcoding error occurs" do
- a = [0xEE].pack('C').force_encoding("utf-8")
- @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a)
+ a = [0xEE].pack('C').force_encoding(Encoding::UTF_8)
+ @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should.equal?(a)
+ a.encoding.should == Encoding::UTF_8
+
+ a = "\x80".b
+ @s.rb_str_conv_enc(a, Encoding::BINARY, Encoding::UTF_8).should.equal?(a)
+ a.encoding.should == Encoding::BINARY
end
it "returns a transcoded String" do
- a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding(Encoding::UTF_8)
result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP)
- x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
- result.should == x.force_encoding("euc-jp")
- result.encoding.should equal(Encoding::EUC_JP)
+ result.should == [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding(Encoding::EUC_JP)
+ result.encoding.should == Encoding::EUC_JP
end
describe "when the String encoding is equal to the destination encoding" do
it "returns the original String" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a)
end
@@ -930,7 +938,7 @@ describe "C-API String function" do
end
it "returns the origin String if the destination encoding is BINARY" do
- a = "abc".force_encoding("binary")
+ a = "abc".dup.force_encoding("binary")
@s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should equal(a)
end
end
@@ -938,7 +946,7 @@ describe "C-API String function" do
describe "rb_str_conv_enc_opts" do
it "returns the original String when to encoding is not specified" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a)
end
@@ -949,7 +957,7 @@ describe "C-API String function" do
end
it "returns a transcoded String" do
- a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8")
+ a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8")
result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil)
x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8')
result.should == x.force_encoding("euc-jp")
@@ -958,7 +966,7 @@ describe "C-API String function" do
describe "when the String encoding is equal to the destination encoding" do
it "returns the original String" do
- a = "abc".force_encoding("us-ascii")
+ a = "abc".dup.force_encoding("us-ascii")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
Encoding::US_ASCII, 0, nil).should equal(a)
end
@@ -970,7 +978,7 @@ describe "C-API String function" do
end
it "returns the origin String if the destination encoding is BINARY" do
- a = "abc".force_encoding("binary")
+ a = "abc".dup.force_encoding("binary")
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
Encoding::BINARY, 0, nil).should equal(a)
end
@@ -988,7 +996,7 @@ describe "C-API String function" do
describe "rb_str_export_locale" do
it "returns the original String with the locale encoding" do
s = @s.rb_str_export_locale("abc")
- s.should == "abc".force_encoding(Encoding.find("locale"))
+ s.should == "abc".dup.force_encoding(Encoding.find("locale"))
s.encoding.should equal(Encoding.find("locale"))
end
end
@@ -1012,7 +1020,7 @@ describe "C-API String function" do
result = @s.rb_str_export_to_enc(source, Encoding::UTF_8)
source.bytes.should == [0, 255]
end
-end
+ end
describe "rb_sprintf" do
it "replaces the parts like sprintf" do
@@ -1040,11 +1048,19 @@ end
@s.rb_sprintf3(true.class).should == s
end
- ruby_bug "#19167", ""..."3.2" do
- it "formats a TrueClass VALUE as 'true' if sign specified in format" do
- s = 'Result: TrueClass.'
- @s.rb_sprintf4(true.class).should == s
- end
+ it "formats a TrueClass VALUE as 'true' if sign specified in format" do
+ s = 'Result: TrueClass.'
+ @s.rb_sprintf4(true.class).should == s
+ end
+
+ it "formats nil using to_s if sign not specified in format" do
+ s = 'Result: .'
+ @s.rb_sprintf3(nil).should == s
+ end
+
+ it "formats nil using inspect if sign specified in format" do
+ s = 'Result: nil.'
+ @s.rb_sprintf4(nil).should == s
end
it "truncates a string to a supplied precision if that is shorter than the string" do
@@ -1114,7 +1130,7 @@ end
end
it "tries to convert the passed argument to a string by calling #to_s" do
- @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}'
+ @s.rb_String({"bar" => "foo"}).should == {"bar" => "foo"}.to_s
end
end
@@ -1203,28 +1219,158 @@ end
describe "rb_str_locktmp" do
it "raises an error when trying to lock an already locked string" do
- str = "test"
+ str = +"test"
@s.rb_str_locktmp(str).should == str
-> { @s.rb_str_locktmp(str) }.should raise_error(RuntimeError, 'temporal locking already locked string')
end
it "locks a string so that modifications would raise an error" do
- str = "test"
+ str = +"test"
@s.rb_str_locktmp(str).should == str
-> { str.upcase! }.should raise_error(RuntimeError, 'can\'t modify string; temporarily locked')
end
+
+ ruby_version_is "4.0" do
+ it "raises FrozenError if string is frozen" do
+ str = -"rb_str_locktmp"
+ -> { @s.rb_str_locktmp(str) }.should raise_error(FrozenError)
+
+ str = +"rb_str_locktmp"
+ str.freeze
+ -> { @s.rb_str_locktmp(str) }.should raise_error(FrozenError)
+ end
+ end
end
describe "rb_str_unlocktmp" do
it "unlocks a locked string" do
- str = "test"
+ str = +"test"
@s.rb_str_locktmp(str)
@s.rb_str_unlocktmp(str).should == str
str.upcase!.should == "TEST"
end
it "raises an error when trying to unlock an already unlocked string" do
- -> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
+ -> { @s.rb_str_unlocktmp(+"test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
+ end
+
+ ruby_version_is "4.0" do
+ it "raises FrozenError if string is frozen" do
+ str = -"rb_str_locktmp"
+ -> { @s.rb_str_unlocktmp(str) }.should raise_error(FrozenError)
+
+ str = +"rb_str_locktmp"
+ str.freeze
+ -> { @s.rb_str_unlocktmp(str) }.should raise_error(FrozenError)
+ end
+ end
+ end
+
+ describe "rb_enc_interned_str_cstr" do
+ it "returns a frozen string" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+
+ val.should.is_a?(String)
+ val.encoding.should == Encoding::US_ASCII
+ val.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8)
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello")
+ end
+
+ ruby_bug "#20322", ""..."3.4" do
+ it "uses the default encoding if encoding is null" do
+ str = "hello"
+ val = @s.rb_enc_interned_str_cstr(str, nil)
+ val.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+ end
+
+ describe "rb_enc_interned_str" do
+ it "returns a frozen string" do
+ str = "hello"
+ val = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII)
+
+ val.should.is_a?(String)
+ val.encoding.should == Encoding::US_ASCII
+ val.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ str = "hello"
+ result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::US_ASCII)
+ result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8)
+ result1.should_not.equal?(result2)
+ end
+
+ it 'returns the same string when using non-ascii characters' do
+ str = 'こんにちは'
+ result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8)
+ result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8)
+ result1.should.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ str = "hello"
+ @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8).should.equal?(-str)
+ end
+
+ ruby_bug "#20322", ""..."3.4" do
+ it "uses the default encoding if encoding is null" do
+ str = "hello"
+ val = @s.rb_enc_interned_str(str, str.bytesize, nil)
+ val.encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+ end
+
+ describe "rb_str_to_interned_str" do
+ it "returns a frozen string" do
+ str = "hello"
+ result = @s.rb_str_to_interned_str(str)
+ result.should.is_a?(String)
+ result.should.frozen?
+ end
+
+ it "returns the same frozen string" do
+ str = "hello"
+ result1 = @s.rb_str_to_interned_str(str)
+ result2 = @s.rb_str_to_interned_str(str)
+ result1.should.equal?(result2)
+ end
+
+ it "returns different frozen strings for different encodings" do
+ result1 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::US_ASCII))
+ result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8))
+ result1.should_not.equal?(result2)
+ end
+
+ it "returns the same string as String#-@" do
+ @s.rb_str_to_interned_str("hello").should.equal?(-"hello")
end
end
end
diff --git a/spec/ruby/optional/capi/struct_spec.rb b/spec/ruby/optional/capi/struct_spec.rb
index 0e9e366908..cc8d7f932e 100644
--- a/spec/ruby/optional/capi/struct_spec.rb
+++ b/spec/ruby/optional/capi/struct_spec.rb
@@ -208,4 +208,109 @@ describe "C-API Struct function" do
@s.rb_struct_size(@struct).should == 3
end
end
+
+ describe "rb_struct_initialize" do
+ it "sets all members" do
+ @s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil
+ @struct.a.should == 1
+ @struct.b.should == 2
+ @struct.c.should == 3
+ end
+
+ it "does not freeze the Struct instance" do
+ @s.rb_struct_initialize(@struct, [1, 2, 3]).should == nil
+ @struct.should_not.frozen?
+ @s.rb_struct_initialize(@struct, [4, 5, 6]).should == nil
+ @struct.a.should == 4
+ @struct.b.should == 5
+ @struct.c.should == 6
+ end
+
+ it "raises ArgumentError if too many values" do
+ -> { @s.rb_struct_initialize(@struct, [1, 2, 3, 4]) }.should raise_error(ArgumentError, "struct size differs")
+ end
+
+ it "treats missing values as nil" do
+ @s.rb_struct_initialize(@struct, [1, 2]).should == nil
+ @struct.a.should == 1
+ @struct.b.should == 2
+ @struct.c.should == nil
+ end
+ end
+end
+
+ruby_version_is "3.3" do
+ describe "C-API Data function" do
+ before :all do
+ @s = CApiStructSpecs.new
+ @klass = @s.rb_data_define(nil, "a", "b", "c")
+ end
+
+ describe "rb_data_define" do
+ it "returns a subclass of Data class when passed nil as the first argument" do
+ @klass.should.is_a? Class
+ @klass.superclass.should == Data
+ end
+
+ it "returns a subclass of a class when passed as the first argument" do
+ superclass = Class.new(Data)
+ klass = @s.rb_data_define(superclass, "a", "b", "c")
+
+ klass.should.is_a? Class
+ klass.superclass.should == superclass
+ end
+
+ it "creates readers for the members" do
+ obj = @klass.new(1, 2, 3)
+
+ obj.a.should == 1
+ obj.b.should == 2
+ obj.c.should == 3
+ end
+
+ it "returns the member names as Symbols" do
+ obj = @klass.new(0, 0, 0)
+
+ obj.members.should == [:a, :b, :c]
+ end
+
+ it "raises an ArgumentError if arguments contain duplicate member name" do
+ -> { @s.rb_data_define(nil, "a", "b", "a") }.should raise_error(ArgumentError)
+ end
+
+ it "raises when first argument is not a class" do
+ -> { @s.rb_data_define([], "a", "b", "c") }.should raise_error(TypeError, "wrong argument type Array (expected Class)")
+ end
+ end
+
+ describe "rb_struct_initialize" do
+ it "sets all members for a Data instance" do
+ data = @klass.allocate
+ @s.rb_struct_initialize(data, [1, 2, 3]).should == nil
+ data.a.should == 1
+ data.b.should == 2
+ data.c.should == 3
+ end
+
+ it "freezes the Data instance" do
+ data = @klass.allocate
+ @s.rb_struct_initialize(data, [1, 2, 3]).should == nil
+ data.should.frozen?
+ -> { @s.rb_struct_initialize(data, [1, 2, 3]) }.should raise_error(FrozenError)
+ end
+
+ it "raises ArgumentError if too many values" do
+ data = @klass.allocate
+ -> { @s.rb_struct_initialize(data, [1, 2, 3, 4]) }.should raise_error(ArgumentError, "struct size differs")
+ end
+
+ it "treats missing values as nil" do
+ data = @klass.allocate
+ @s.rb_struct_initialize(data, [1, 2]).should == nil
+ data.a.should == 1
+ data.b.should == 2
+ data.c.should == nil
+ end
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/thread_spec.rb b/spec/ruby/optional/capi/thread_spec.rb
index 5cb46bbb7c..117726f0e2 100644
--- a/spec/ruby/optional/capi/thread_spec.rb
+++ b/spec/ruby/optional/capi/thread_spec.rb
@@ -165,24 +165,30 @@ describe "C-API Thread function" do
end
end
- platform_is_not :mingw do
- it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do
- thr = Thread.new do
- @t.rb_thread_call_without_gvl_with_ubf_io
- end
+ it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do
+ thr = Thread.new do
+ @t.rb_thread_call_without_gvl_with_ubf_io
+ end
+
+ # Wait until it's blocking...
+ Thread.pass until thr.stop?
- # Wait until it's blocking...
- Thread.pass until thr.stop?
+ # The thread status is set to sleep by rb_thread_call_without_gvl(),
+ # but the thread might not be in the blocking read(2) yet, so wait a bit.
+ sleep 0.1
- # The thread status is set to sleep by rb_thread_call_without_gvl(),
- # but the thread might not be in the blocking read(2) yet, so wait a bit.
- sleep 0.1
+ # Wake it up, causing the unblock function to be run.
+ thr.wakeup
- # Wake it up, causing the unblock function to be run.
- thr.wakeup
+ # Make sure it stopped and we got a proper value
+ thr.value.should be_true
+ end
+ end
- # Make sure it stopped and we got a proper value
- thr.value.should be_true
+ ruby_version_is "4.0" do
+ describe "ruby_thread_has_gvl_p" do
+ it "returns true if the current thread has the GVL" do
+ @t.ruby_thread_has_gvl_p.should be_true
end
end
end
diff --git a/spec/ruby/optional/capi/time_spec.rb b/spec/ruby/optional/capi/time_spec.rb
index 579e81fc19..ca5fc5952a 100644
--- a/spec/ruby/optional/capi/time_spec.rb
+++ b/spec/ruby/optional/capi/time_spec.rb
@@ -283,6 +283,11 @@ describe "CApiTimeSpecs" do
-> { @s.rb_time_timespec_new(1447087832, 476451125, 86400) }.should raise_error(ArgumentError)
-> { @s.rb_time_timespec_new(1447087832, 476451125, -86400) }.should raise_error(ArgumentError)
end
+
+ it "doesn't call Time.at directly" do
+ Time.should_not_receive(:at)
+ @s.rb_time_timespec_new(1447087832, 476451125, 32400).should be_kind_of(Time)
+ end
end
describe "rb_timespec_now" do
diff --git a/spec/ruby/optional/capi/typed_data_spec.rb b/spec/ruby/optional/capi/typed_data_spec.rb
index 23b7c157ef..6d1398a1a0 100644
--- a/spec/ruby/optional/capi/typed_data_spec.rb
+++ b/spec/ruby/optional/capi/typed_data_spec.rb
@@ -85,4 +85,16 @@ describe "CApiWrappedTypedStruct" do
-> { @s.rb_check_typeddata_different_type(a) }.should raise_error(TypeError)
end
end
+
+ describe "RTYPEDDATA_P" do
+ it "returns true for a typed data" do
+ a = @s.typed_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == true
+ end
+
+ it "returns false for an untyped data object" do
+ a = @s.untyped_wrap_struct(1024)
+ @s.RTYPEDDATA_P(a).should == false
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/util_spec.rb b/spec/ruby/optional/capi/util_spec.rb
index 38f6f47b1a..6cf064bf97 100644
--- a/spec/ruby/optional/capi/util_spec.rb
+++ b/spec/ruby/optional/capi/util_spec.rb
@@ -48,7 +48,7 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, 2, [3, 4]]
end
- it "assigns the required and optional arguments and and empty Array when there are no arguments to splat" do
+ it "assigns the required and optional arguments and empty Array when there are no arguments to splat" do
@o.rb_scan_args([1, 2], "11*", 3, @acc).should == 2
ScratchPad.recorded.should == [1, 2, []]
end
@@ -115,22 +115,11 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, nil]
end
- ruby_version_is ''...'3.0' do
- it "assigns required and Hash arguments with nil Hash" do
- suppress_warning do
- @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
- end
- ScratchPad.recorded.should == [1, nil]
- end
- end
-
- ruby_version_is '3.0' do
- it "rejects the use of nil as a hash" do
- -> {
- @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
+ it "rejects the use of nil as a hash" do
+ -> {
+ @o.rb_scan_args([1, nil], "1:", 2, @acc).should == 1
+ }.should raise_error(ArgumentError)
+ ScratchPad.recorded.should == []
end
it "assigns required and optional arguments with no hash argument given" do
@@ -138,61 +127,41 @@ describe "C-API Util function" do
ScratchPad.recorded.should == [1, 7, 4]
end
+ it "assigns optional arguments with no hash argument given" do
+ @o.rb_scan_args([1, 7], "02:", 3, @acc).should == 2
+ ScratchPad.recorded.should == [1, 7, nil]
+ end
+
+ it "assigns optional arguments with no hash argument given and rejects the use of optional nil argument as a hash" do
+ -> {
+ @o.rb_scan_args([1, nil], "02:", 3, @acc).should == 2
+ }.should_not complain
+
+ ScratchPad.recorded.should == [1, nil, nil]
+ end
+
it "assigns required, optional, splat, post-splat, Hash and block arguments" do
h = {a: 1, b: 2}
@o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
end
- ruby_version_is ''...'3.0' do
- # r43934
- it "rejects non-keyword arguments" do
- h = {1 => 2, 3 => 4}
- -> {
- suppress_warning do
- @o.rb_scan_args([h], "k0:", 1, @acc)
- end
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
-
- it "rejects required and non-keyword arguments" do
- h = {1 => 2, 3 => 4}
- -> {
- suppress_warning do
- @o.rb_scan_args([1, h], "k1:", 2, @acc)
- end
- }.should raise_error(ArgumentError)
- ScratchPad.recorded.should == []
- end
-
- it "considers the hash as a post argument when there is a splat" do
- h = {1 => 2, 3 => 4}
- suppress_warning do
- @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 6
- end
- ScratchPad.recorded.should == [1, 2, [3, 4, 5], h, nil, @prc]
- end
+ it "does not reject non-symbol keys in keyword arguments" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([h], "k0:", 1, @acc).should == 0
+ ScratchPad.recorded.should == [h]
end
- ruby_version_is '3.0' do
- it "does not reject non-symbol keys in keyword arguments" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([h], "k0:", 1, @acc).should == 0
- ScratchPad.recorded.should == [h]
- end
-
- it "does not reject non-symbol keys in keyword arguments with required argument" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1
- ScratchPad.recorded.should == [1, h]
- end
+ it "does not reject non-symbol keys in keyword arguments with required argument" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([1, h], "k1:", 2, @acc).should == 1
+ ScratchPad.recorded.should == [1, h]
+ end
- it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do
- h = {1 => 2, 3 => 4}
- @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
- ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
- end
+ it "considers keyword arguments with non-symbol keys as keywords when using splat and post arguments" do
+ h = {1 => 2, 3 => 4}
+ @o.rb_scan_args([1, 2, 3, 4, 5, h], "k11*1:&", 6, @acc, &@prc).should == 5
+ ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc]
end
end
@@ -240,7 +209,7 @@ describe "C-API Util function" do
end
end
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
describe "rb_long2int" do
it "raises a RangeError if the value is outside the range of a C int" do
-> { @o.rb_long2int(0xffff_ffff_ffff) }.should raise_error(RangeError)
diff --git a/spec/ruby/optional/thread_safety/fixtures/classes.rb b/spec/ruby/optional/thread_safety/fixtures/classes.rb
new file mode 100644
index 0000000000..4f0ad030e5
--- /dev/null
+++ b/spec/ruby/optional/thread_safety/fixtures/classes.rb
@@ -0,0 +1,39 @@
+module ThreadSafetySpecs
+ # Returns the number of processors, rounded up so it's always a multiple of 2
+ def self.processors
+ require 'etc'
+ n = Etc.nprocessors
+ raise "expected at least 1 processor" if n < 1
+ n += 1 if n.odd? # ensure it's a multiple of 2
+ n
+ end
+
+ class Counter
+ def initialize
+ @value = 0
+ @mutex = Mutex.new
+ end
+
+ def get
+ @mutex.synchronize { @value }
+ end
+
+ def increment
+ @mutex.synchronize do
+ @value += 1
+ end
+ end
+ end
+
+ class Barrier
+ def initialize(parties)
+ @parties = parties
+ @counter = Counter.new
+ end
+
+ def wait
+ @counter.increment
+ Thread.pass until @counter.get == @parties
+ end
+ end
+end
diff --git a/spec/ruby/optional/thread_safety/hash_spec.rb b/spec/ruby/optional/thread_safety/hash_spec.rb
new file mode 100644
index 0000000000..53127fc973
--- /dev/null
+++ b/spec/ruby/optional/thread_safety/hash_spec.rb
@@ -0,0 +1,210 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Hash thread safety" do
+ it "supports concurrent #[]=" do
+ n_threads = ThreadSafetySpecs.processors
+
+ n = 1_000
+ operations = n * n_threads
+
+ h = {}
+ barrier = ThreadSafetySpecs::Barrier.new(n_threads + 1)
+
+ threads = n_threads.times.map { |t|
+ Thread.new {
+ barrier.wait
+ base = t * n
+ n.times do |j|
+ h[base+j] = t
+ end
+ }
+ }
+
+ barrier.wait
+ threads.each(&:join)
+
+ h.size.should == operations
+ h.each_pair { |key, value|
+ (key / n).should == value
+ }
+ end
+
+ # can't add a new key into hash during iteration (RuntimeError) on CRuby.
+ # Yet it's good to test this for implementations that support it.
+ guard_not -> { PlatformGuard.standard? } do
+ it "supports concurrent #[]= and #delete and iteration" do
+ n_threads = ThreadSafetySpecs.processors
+
+ n = 1_000
+ operations = n * n_threads / 2
+
+ h = {}
+ barrier1 = ThreadSafetySpecs::Barrier.new(n_threads + 2)
+ barrier2 = ThreadSafetySpecs::Barrier.new(n_threads + 1)
+
+ threads = n_threads.times.map { |t|
+ Thread.new {
+ barrier1.wait
+ base = t * n
+ n.times do |j|
+ h[base+j] = t
+ end
+
+ barrier2.wait
+ n.times do |j|
+ # delete only even keys
+ h.delete(base+j).should == t if (base+j).even?
+ end
+ }
+ }
+
+ read = true
+ reader = Thread.new {
+ barrier1.wait
+ while read
+ h.each_pair { |k,v|
+ k.should.is_a?(Integer)
+ v.should.is_a?(Integer)
+ (k / n).should == v
+ }
+ end
+ }
+
+ barrier1.wait
+ barrier2.wait
+ threads.each(&:join)
+ read = false
+ reader.join
+
+ # odd keys are left
+ h.size.should == operations
+ h.each_pair { |key, value|
+ key.should.odd?
+ (key / n).should == value
+ }
+ end
+ end
+
+ it "supports concurrent #[]= and #[]" do
+ n_threads = ThreadSafetySpecs.processors
+
+ n = 1_000
+ operations = n * n_threads / 2
+
+ h = {}
+ barrier = ThreadSafetySpecs::Barrier.new(n_threads + 1)
+
+ threads = n_threads.times.map { |t|
+ Thread.new {
+ barrier.wait
+ base = (t / 2) * n
+
+ if t.even?
+ n.times do |j|
+ k = base + j
+ h[k] = k
+ end
+ else
+ n.times do |j|
+ k = base + j
+ Thread.pass until v = h[k]
+ v.should == k
+ end
+ end
+ }
+ }
+
+ barrier.wait
+ threads.each(&:join)
+
+ h.size.should == operations
+ h.each_pair { |key, value|
+ key.should == value
+ }
+ end
+
+ it "supports concurrent #[]= and #[] with change to #compare_by_identity in the middle" do
+ n_threads = ThreadSafetySpecs.processors
+
+ n = 1_000
+ operations = n * n_threads / 2
+
+ h = {}
+ barrier = ThreadSafetySpecs::Barrier.new(n_threads + 1)
+
+ threads = n_threads.times.map { |t|
+ Thread.new {
+ barrier.wait
+ base = (t / 2) * n
+
+ if t.even?
+ n.times do |j|
+ k = base + j
+ h[k] = k
+ end
+ else
+ n.times do |j|
+ k = base + j
+ Thread.pass until v = h[k]
+ v.should == k
+ end
+ end
+ }
+ }
+
+ changer = Thread.new {
+ Thread.pass until h.size >= operations / 2
+ h.should_not.compare_by_identity?
+ h.compare_by_identity
+ h.should.compare_by_identity?
+ }
+
+ barrier.wait
+ threads.each(&:join)
+ changer.join
+
+ h.size.should == operations
+ h.each_pair { |key, value|
+ key.should == value
+ }
+ end
+
+ it "supports repeated concurrent #[]= and #delete and always returns a #size >= 0" do
+ n_threads = ThreadSafetySpecs.processors
+
+ n = 1_000
+ operations = n * n_threads / 2
+
+ h = {}
+ barrier = ThreadSafetySpecs::Barrier.new(n_threads + 1)
+ deleted = ThreadSafetySpecs::Counter.new
+
+ threads = n_threads.times.map { |t|
+ Thread.new {
+ barrier.wait
+ key = t / 2
+
+ if t.even?
+ n.times {
+ Thread.pass until h.delete(key)
+ h.size.should >= 0
+ deleted.increment
+ }
+ else
+ n.times {
+ h[key] = key
+ Thread.pass while h.key?(key)
+ }
+ end
+ }
+ }
+
+ barrier.wait
+ threads.each(&:join)
+
+ deleted.get.should == operations
+ h.size.should == 0
+ h.should.empty?
+ end
+end