diff options
Diffstat (limited to 'spec/ruby/optional/capi')
50 files changed, 1625 insertions, 499 deletions
diff --git a/spec/ruby/optional/capi/array_spec.rb b/spec/ruby/optional/capi/array_spec.rb index 8e90980c6a..9c35017e21 100644 --- a/spec/ruby/optional/capi/array_spec.rb +++ b/spec/ruby/optional/capi/array_spec.rb @@ -343,6 +343,40 @@ describe "C-API Array function" do end end + 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 + 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/class_spec.rb b/spec/ruby/optional/capi/class_spec.rb index a57b8f644f..d0a9913570 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 @@ -196,16 +194,6 @@ describe "C-API Class function" do end end - describe "rb_define_method" do - it "defines a method taking variable arguments as a C array if the argument count is -1" do - @s.rb_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4] - end - - it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do - @s.rb_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4] - end - end - describe "rb_class2name" do it "returns the class name" do @s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs" @@ -214,6 +202,10 @@ describe "C-API Class function" do it "returns a string for an anonymous class" do @s.rb_class2name(Class.new).should be_kind_of(String) end + + it "returns a string beginning with # for an anonymous class" do + @s.rb_class2name(Struct.new(:x, :y).new(1, 2).class).should.start_with?('#') + end end describe "rb_class_path" do @@ -329,6 +321,15 @@ describe "C-API Class function" do @s.rb_define_class("ClassSpecDefineClass4", nil) }.should raise_error(ArgumentError) end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class("_INVALID_CLASS", CApiClassSpecs::Super) + cls.name.should == "_INVALID_CLASS" + + -> { + Object.const_get(cls.name) + }.should raise_error(NameError, /wrong constant name/) + end end describe "rb_define_class_under" do @@ -373,6 +374,15 @@ describe "C-API Class function" do it "raises a TypeError if class is defined and its superclass mismatches the given one" do -> { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should raise_error(TypeError) end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class_under(CApiClassSpecs, "_INVALID_CLASS", CApiClassSpecs::Super) + cls.name.should == "CApiClassSpecs::_INVALID_CLASS" + + -> { + CApiClassSpecs.const_get(cls.name) + }.should raise_error(NameError, /wrong constant name/) + end end describe "rb_define_class_id_under" do @@ -400,6 +410,15 @@ describe "C-API Class function" do it "raises a TypeError if class is defined and its superclass mismatches the given one" do -> { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should raise_error(TypeError) end + + it "allows arbitrary names, including constant names not valid in Ruby" do + cls = @s.rb_define_class_id_under(CApiClassSpecs, :_INVALID_CLASS2, CApiClassSpecs::Super) + cls.name.should == "CApiClassSpecs::_INVALID_CLASS2" + + -> { + CApiClassSpecs.const_get(cls.name) + }.should raise_error(NameError, /wrong constant name/) + end end describe "rb_define_class_variable" do diff --git a/spec/ruby/optional/capi/debug_spec.rb b/spec/ruby/optional/capi/debug_spec.rb index c8c91417d1..148b8c38fb 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,14 @@ 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 = @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]}" + binding.source_location.should == [backtrace_location.path, backtrace_location.lineno] + 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/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb index 66c2dd40de..1529e012b0 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 @@ -63,6 +64,48 @@ describe "C-API Encoding function" do end end + describe "rb_enc_strlen" do + before :each do + @str = 'こにちわ' # Each codepoint in this string is 3 bytes in UTF-8 + end + + it "returns the correct string length for the encoding" do + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_8).should == 4 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::BINARY).should == 12 + end + + it "returns the string length based on a fixed-width encoding's character length, even if the encoding is incompatible" do + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16BE).should == 6 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16LE).should == 6 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32BE).should == 3 + @s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32LE).should == 3 + end + + it "does not consider strings to be NUL-terminated" do + s = "abc\0def" + @s.rb_enc_strlen(s, s.bytesize, Encoding::US_ASCII).should == 7 + @s.rb_enc_strlen(s, s.bytesize, Encoding::UTF_8).should == 7 + end + + describe "handles broken strings" do + it "combines valid character and invalid character counts in UTF-8" do + # The result is 3 because `rb_enc_strlen` counts the first valid character and then adds + # the byte count for the invalid character that follows for 1 + 2. + @s.rb_enc_strlen(@str, 5, Encoding::UTF_8).should == 3 + end + + it "combines valid character and invalid character counts in UTF-16" do + @s.rb_enc_strlen(@str, 5, Encoding::UTF_16BE).should == 3 + end + + it "rounds up for fixed-width encodings" do + @s.rb_enc_strlen(@str, 7, Encoding::UTF_32BE).should == 2 + @s.rb_enc_strlen(@str, 7, Encoding::UTF_32LE).should == 2 + @s.rb_enc_strlen(@str, 5, Encoding::BINARY).should == 5 + end + end + end + describe "rb_enc_find" do it "returns the encoding of an Encoding" do @s.rb_enc_find("UTF-8").should == "UTF-8" @@ -128,10 +171,16 @@ 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("é", 2).should == 0x00E9 - @s.rb_enc_mbc_to_codepoint("éa", 2).should == 0x00E9 - @s.rb_enc_mbc_to_codepoint("éa", 1).should == 0xC3 - @s.rb_enc_mbc_to_codepoint("éa", 3).should == 0x00E9 + @s.rb_enc_mbc_to_codepoint("é").should == 0xE9 + end + + it "returns 0 if p == e" do + @s.rb_enc_mbc_to_codepoint("").should == 0 + end + + it "returns the raw byte if incomplete character in UTF-8" do + @s.rb_enc_mbc_to_codepoint("\xC3").should == 0xC3 + @s.rb_enc_mbc_to_codepoint("\x80").should == 0x80 end end @@ -511,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 @@ -609,27 +658,58 @@ 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 { - 0 => "\x01", - 0x7f => "\xC2\x80", - 0x7ff => "\xE0\xA0\x80", - 0xffff => "\xF0\x90\x80\x80", - 0x1fffff => "\xF8\x88\x80\x80\x80", - 0x3ffffff => "\xFC\x84\x80\x80\x80\x80", + 1 => "\x01", + 0x80 => "\xC2\x80", + 0x800 => "\xE0\xA0\x80", + 0x10000 => "\xF0\x90\x80\x80", + 0x200000 => "\xF8\x88\x80\x80\x80", + 0x4000000 => "\xFC\x84\x80\x80\x80\x80", }.each do |num, result| - len = @s.rb_uv_to_utf8(str, num + 1) - str[0..len-1].should == result + len = @s.rb_uv_to_utf8(str, num) + str.byteslice(0, len).should == result end 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] @s.ONIGENC_MBC_CASE_FOLD("Upper").should == ["u", 1] + @s.ONIGENC_MBC_CASE_FOLD("ABC"[1..-1]).should == ["b", 1] end it "works with other encodings" do @@ -642,4 +722,27 @@ describe "C-API Encoding function" do str.bytes.should == [0, 0x24] end end + + describe "rb_define_dummy_encoding" do + it "defines the dummy encoding" do + @s.rb_define_dummy_encoding("FOO") + enc = Encoding.find("FOO") + enc.should.dummy? + end + + it "returns the index of the dummy encoding" do + index = @s.rb_define_dummy_encoding("BAR") + index.should == Encoding.list.size - 1 + end + + ruby_version_is "3.2" do + 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 end diff --git a/spec/ruby/optional/capi/exception_spec.rb b/spec/ruby/optional/capi/exception_spec.rb index b0a8a2860e..5bb60608b2 100644 --- a/spec/ruby/optional/capi/exception_spec.rb +++ b/spec/ruby/optional/capi/exception_spec.rb @@ -100,6 +100,40 @@ describe "C-API Exception function" do 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..2347798bb4 100644 --- a/spec/ruby/optional/capi/ext/array_spec.c +++ b/spec/ruby/optional/capi/ext/array_spec.c @@ -196,6 +196,14 @@ static VALUE copy_ary(RB_BLOCK_CALL_FUNC_ARGLIST(el, new_ary)) { return rb_ary_push(new_ary, el); } +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; +} + static VALUE array_spec_rb_block_call(VALUE self, VALUE ary) { VALUE new_ary = rb_ary_new(); @@ -208,6 +216,18 @@ static VALUE sub_pair(RB_BLOCK_CALL_FUNC_ARGLIST(el, holder)) { return rb_ary_push(holder, rb_ary_entry(el, 1)); } +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; +} + static VALUE array_spec_rb_block_call_each_pair(VALUE self, VALUE obj) { VALUE new_ary = rb_ary_new(); @@ -221,6 +241,11 @@ static VALUE iter_yield(RB_BLOCK_CALL_FUNC_ARGLIST(el, ary)) { return Qnil; } +static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) { + rb_iterate(rb_each, obj, iter_yield, obj); + return Qnil; +} + 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 +308,9 @@ 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); + 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); 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 b860742906..f376534924 100644 --- a/spec/ruby/optional/capi/ext/class_spec.c +++ b/spec/ruby/optional/capi/ext/class_spec.c @@ -72,7 +72,7 @@ static VALUE class_spec_rb_class_new_instance_kw(VALUE self, VALUE args, VALUE k #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)); @@ -116,19 +116,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); } @@ -142,19 +142,6 @@ static VALUE class_spec_include_module(VALUE self, VALUE klass, VALUE module) { return klass; } -static VALUE class_spec_method_var_args_1(int argc, VALUE *argv, VALUE self) { - VALUE ary = rb_ary_new(); - int i; - for (i = 0; i < argc; i++) { - rb_ary_push(ary, argv[i]); - } - return ary; -} - -static VALUE class_spec_method_var_args_2(VALUE self, VALUE argv) { - return argv; -} - void Init_class_spec(void) { VALUE cls = rb_define_class("CApiClassSpecs", rb_cObject); rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2); @@ -185,8 +172,6 @@ void Init_class_spec(void) { rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3); rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3); rb_define_method(cls, "rb_include_module", class_spec_include_module, 2); - rb_define_method(cls, "rb_method_varargs_1", class_spec_method_var_args_1, -1); - rb_define_method(cls, "rb_method_varargs_2", class_spec_method_var_args_2, -2); } #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/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c index cde4d0c351..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,21 +61,19 @@ 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); } -#ifdef RUBY_VERSION_IS_2_6 static VALUE encoding_spec_rb_enc_alias(VALUE self, VALUE alias, VALUE orig) { return INT2NUM(rb_enc_alias(RSTRING_PTR(alias), RSTRING_PTR(orig))); } -#endif static VALUE encoding_spec_rb_enc_associate(VALUE self, VALUE obj, VALUE enc) { return rb_enc_associate(obj, NIL_P(enc) ? NULL : rb_enc_find(RSTRING_PTR(enc))); @@ -88,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); } @@ -120,10 +118,9 @@ static VALUE encoding_spec_rb_enc_from_index(VALUE self, VALUE index) { return rb_str_new2(rb_enc_from_index(NUM2INT(index))->name); } -static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str, VALUE offset) { - int o = FIX2INT(offset); +static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str) { char *p = RSTRING_PTR(str); - char *e = p + o; + char *e = RSTRING_END(str); return INT2FIX(rb_enc_mbc_to_codepoint(p, e, rb_enc_get(str))); } @@ -274,8 +271,17 @@ 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) { - return INT2NUM(rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num))); + int len = rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num)); + RB_ENC_CODERANGE_CLEAR(buf); + return INT2NUM(len); } static VALUE encoding_spec_ONIGENC_MBC_CASE_FOLD(VALUE self, VALUE str) { @@ -300,6 +306,24 @@ static VALUE encoding_spec_rb_enc_codelen(VALUE self, VALUE code, VALUE encoding return INT2FIX(rb_enc_codelen(c, enc)); } +static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VALUE encoding) { + int l = FIX2INT(length); + char *p = RSTRING_PTR(str); + char *e = p + l; + + 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*)); @@ -318,28 +342,22 @@ void Init_encoding_spec(void) { rb_define_method(cls, "rb_locale_encindex", encoding_spec_rb_locale_encindex, 0); rb_define_method(cls, "rb_filesystem_encoding", encoding_spec_rb_filesystem_encoding, 0); rb_define_method(cls, "rb_filesystem_encindex", encoding_spec_rb_filesystem_encindex, 0); - rb_define_method(cls, "rb_default_internal_encoding", - encoding_spec_rb_default_internal_encoding, 0); - - rb_define_method(cls, "rb_default_external_encoding", - encoding_spec_rb_default_external_encoding, 0); - -#ifdef RUBY_VERSION_IS_2_6 + rb_define_method(cls, "rb_default_internal_encoding", encoding_spec_rb_default_internal_encoding, 0); + rb_define_method(cls, "rb_default_external_encoding", encoding_spec_rb_default_external_encoding, 0); rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2); -#endif - rb_define_method(cls, "MBCLEN_CHARFOUND_P", encoding_spec_MBCLEN_CHARFOUND_P, 1); rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2); rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2); rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2); rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2); rb_define_method(cls, "rb_enc_codelen", encoding_spec_rb_enc_codelen, 2); + rb_define_method(cls, "rb_enc_strlen", encoding_spec_rb_enc_strlen, 3); rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1); rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1); rb_define_method(cls, "rb_enc_isalnum", encoding_spec_rb_enc_isalnum, 2); rb_define_method(cls, "rb_enc_isspace", encoding_spec_rb_enc_isspace, 2); rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1); - rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 2); + rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 1); rb_define_method(cls, "rb_enc_mbcput", encoding_spec_rb_enc_mbcput, 2); rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1); rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1); @@ -361,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..0e8347ab0d 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,21 @@ VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) { 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 +66,8 @@ 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_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/gc_spec.c b/spec/ruby/optional/capi/ext/gc_spec.c index 7dc9c347c7..1392bc6ee6 100644 --- a/spec/ruby/optional/capi/ext/gc_spec.c +++ b/spec/ruby/optional/capi/ext/gc_spec.c @@ -7,6 +7,14 @@ extern "C" { VALUE registered_tagged_value; VALUE registered_reference_value; +VALUE registered_before_rb_gc_register_address; +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; static VALUE registered_tagged_address(VALUE self) { return registered_tagged_value; @@ -16,6 +24,45 @@ static VALUE registered_reference_address(VALUE self) { return registered_reference_value; } +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_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(&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; +} + +static VALUE gc_spec_rb_gc_unregister_address(VALUE self) { + rb_gc_unregister_address(&rb_gc_register_address_outside_init); + return Qnil; +} + static VALUE gc_spec_rb_gc_enable(VALUE self) { return rb_gc_enable(); } @@ -29,7 +76,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); } @@ -45,14 +92,40 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) { 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(®istered_tagged_value); rb_gc_register_address(®istered_reference_value); + rb_gc_register_address(®istered_before_rb_gc_register_address); + rb_global_variable(®istered_before_rb_global_variable_string); + rb_global_variable(®istered_before_rb_global_variable_bignum); + rb_global_variable(®istered_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_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(®istered_after_rb_global_variable_string); + registered_after_rb_global_variable_bignum = LL2NUM(INT64_MAX); + rb_global_variable(®istered_after_rb_global_variable_bignum); + registered_after_rb_global_variable_float = DBL2NUM(6.28); + rb_global_variable(®istered_after_rb_global_variable_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_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); rb_define_method(cls, "rb_gc_disable", gc_spec_rb_gc_disable, 0); rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0); diff --git a/spec/ruby/optional/capi/ext/globals_spec.c b/spec/ruby/optional/capi/ext/globals_spec.c index 28a9633f98..20dea1a05a 100644 --- a/spec/ruby/optional/capi/ext/globals_spec.c +++ b/spec/ruby/optional/capi/ext/globals_spec.c @@ -20,6 +20,16 @@ static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) { return Qnil; } +static VALUE sb_define_hooked_variable_default_accessors(VALUE self, VALUE var_name) { + rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + +static VALUE sb_define_hooked_variable_null_var(VALUE self, VALUE var_name) { + rb_define_hooked_variable(StringValuePtr(var_name), NULL, (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + VALUE g_ro_var; static VALUE sb_define_readonly_variable(VALUE self, VALUE var_name, VALUE val) { @@ -40,6 +50,26 @@ static VALUE sb_define_variable(VALUE self, VALUE var_name, VALUE val) { return Qnil; } +long virtual_var_storage; + +VALUE incrementing_getter(ID id, VALUE *data) { + return LONG2FIX(virtual_var_storage++); +} + +void incrementing_setter(VALUE val, ID id, VALUE *data) { + virtual_var_storage = FIX2LONG(val); +} + +static VALUE sb_define_virtual_variable_default_accessors(VALUE self, VALUE name) { + rb_define_virtual_variable(StringValuePtr(name), (rb_gvar_getter_t*) NULL, (rb_gvar_setter_t*) NULL); + return Qnil; +} + +static VALUE sb_define_virtual_variable_incrementing_accessors(VALUE self, VALUE name) { + rb_define_virtual_variable(StringValuePtr(name), incrementing_getter, incrementing_setter); + return Qnil; +} + static VALUE sb_f_global_variables(VALUE self) { return rb_f_global_variables(); } @@ -101,10 +131,14 @@ void Init_globals_spec(void) { VALUE cls = rb_define_class("CApiGlobalSpecs", rb_cObject); g_hooked_var = Qnil; rb_define_method(cls, "rb_define_hooked_variable_2x", sb_define_hooked_variable, 1); + rb_define_method(cls, "rb_define_hooked_variable_default_accessors", sb_define_hooked_variable_default_accessors, 1); + rb_define_method(cls, "rb_define_hooked_variable_null_var", sb_define_hooked_variable_null_var, 1); g_ro_var = Qnil; rb_define_method(cls, "rb_define_readonly_variable", sb_define_readonly_variable, 2); g_var = Qnil; rb_define_method(cls, "rb_define_variable", sb_define_variable, 2); + rb_define_method(cls, "rb_define_virtual_variable_default_accessors", sb_define_virtual_variable_default_accessors, 1); + rb_define_method(cls, "rb_define_virtual_variable_incrementing_accessors", sb_define_virtual_variable_incrementing_accessors, 1); rb_define_method(cls, "sb_get_global_value", sb_get_global_value, 0); rb_define_method(cls, "rb_f_global_variables", sb_f_global_variables, 0); rb_define_method(cls, "sb_gv_get", sb_gv_get, 1); diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c index 7f38708915..69ef02d5da 100644 --- a/spec/ruby/optional/capi/ext/hash_spec.c +++ b/spec/ruby/optional/capi/ext/hash_spec.c @@ -105,6 +105,12 @@ VALUE hash_spec_rb_hash_new(VALUE self) { return rb_hash_new(); } +#ifdef RUBY_VERSION_IS_3_2 +VALUE hash_spec_rb_hash_new_capa(VALUE self, VALUE capacity) { + return rb_hash_new_capa(NUM2LONG(capacity)); +} +#endif + VALUE rb_ident_hash_new(void); /* internal.h, used in ripper */ VALUE hash_spec_rb_ident_hash_new(VALUE self) { @@ -149,6 +155,9 @@ 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); +#ifdef RUBY_VERSION_IS_3_2 + rb_define_method(cls, "rb_hash_new_capa", hash_spec_rb_hash_new_capa, 1); +#endif 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); 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 b4ffe9207a..bcd3940e34 100644 --- a/spec/ruby/optional/capi/ext/io_spec.c +++ b/spec/ruby/optional/capi/ext/io_spec.c @@ -28,9 +28,13 @@ static int set_non_blocking(int fd) { } static int io_spec_get_fd(VALUE io) { +#ifdef RUBY_VERSION_IS_3_1 + return rb_io_descriptor(io); +#else rb_io_t* fp; GetOpenFile(io, fp); return fp->fd; +#endif } VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) { @@ -130,7 +134,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; } @@ -141,7 +145,7 @@ VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { ret = rb_io_wait_readable(fd); - 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"); @@ -162,6 +166,60 @@ VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) { return ret ? Qtrue : Qfalse; } +#ifdef RUBY_VERSION_IS_3_1 +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); +} +#endif + +#ifdef RUBY_VERSION_IS_3_1 +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; +#endif +} +#endif + +#ifdef RUBY_VERSION_IS_3_1 +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); +} +#endif + VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) { rb_thread_wait_fd(io_spec_get_fd(io)); return Qnil; @@ -182,6 +240,46 @@ VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { return Qnil; } +VALUE io_spec_rb_thread_fd_select_read(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, &fds, NULL, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_write(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_timeout(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 20; + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, &timeout); + rb_fd_term(&fds); + return INT2FIX(r); +} + VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) { return rb_io_binmode(io); } @@ -209,7 +307,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; @@ -228,15 +326,29 @@ static VALUE io_spec_errno_set(VALUE self, VALUE val) { } VALUE io_spec_mode_sync_flag(VALUE self, VALUE io) { +#ifdef RUBY_VERSION_IS_3_3 + if (rb_io_mode(io) & FMODE_SYNC) { +#else rb_io_t *fp; GetOpenFile(io, fp); if (fp->mode & FMODE_SYNC) { +#endif 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); +} +#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); @@ -254,14 +366,26 @@ 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); +#ifdef RUBY_VERSION_IS_3_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); +#endif 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); + rb_define_method(cls, "rb_thread_fd_select_write", io_spec_rb_thread_fd_select_write, 1); + rb_define_method(cls, "rb_thread_fd_select_timeout", io_spec_rb_thread_fd_select_timeout, 1); rb_define_method(cls, "rb_wait_for_single_fd", io_spec_rb_wait_for_single_fd, 4); rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1); rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1); 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); +#endif } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index 46af8696a5..1761599081 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -112,6 +112,10 @@ VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) { return rb_eval_string(RSTRING_PTR(str)); } +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)); +} + VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) { rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before"))); if (self != Qundef) @@ -216,7 +220,7 @@ 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) { + if (msg == Qnil) { rb_sys_fail(0); } else if (self != Qundef) { rb_sys_fail(StringValuePtr(msg)); @@ -225,7 +229,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)); @@ -288,9 +292,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; @@ -336,8 +340,12 @@ 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) { @@ -347,6 +355,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); @@ -361,6 +378,7 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_frame_this_func_test_again", kernel_spec_rb_frame_this_func, 0); rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4); rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1); + rb_define_method(cls, "rb_eval_cmd_kw", kernel_spec_rb_eval_cmd_kw, 3); 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); @@ -392,7 +410,9 @@ void Init_kernel_spec(void) { #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/module_spec.c b/spec/ruby/optional/capi/ext/module_spec.c index 7475994fa1..12bcf99983 100644 --- a/spec/ruby/optional/capi/ext/module_spec.c +++ b/spec/ruby/optional/capi/ext/module_spec.c @@ -9,18 +9,6 @@ static VALUE module_specs_test_method(VALUE self) { return ID2SYM(rb_intern("test_method")); } -static VALUE module_specs_test_method_2required(VALUE self, VALUE arg1, VALUE arg2) { - return ID2SYM(rb_intern("test_method_2required")); -} - -static VALUE module_specs_test_method_c_array(int argc, VALUE *argv, VALUE self) { - return ID2SYM(rb_intern("test_method_c_array")); -} - -static VALUE module_specs_test_method_ruby_array(VALUE self, VALUE args) { - return ID2SYM(rb_intern("test_method_ruby_array")); -} - static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) { return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse; } @@ -88,19 +76,25 @@ static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name return Qnil; } -static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE cls, VALUE str_name) { - rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_2required, 2); - return Qnil; +static VALUE module_specs_method_var_args_1(int argc, VALUE *argv, VALUE self) { + VALUE ary = rb_ary_new(); + int i; + for (i = 0; i < argc; i++) { + rb_ary_push(ary, argv[i]); + } + return ary; } -static VALUE module_specs_rb_define_method_c_array(VALUE self, VALUE cls, VALUE str_name) { - rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_c_array, -1); - return Qnil; +static VALUE module_specs_method_var_args_2(VALUE self, VALUE argv) { + return argv; } -static VALUE module_specs_rb_define_method_ruby_array(VALUE self, VALUE cls, VALUE str_name) { - rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_ruby_array, -2); - return Qnil; +static VALUE module_specs_rb_define_method_1required(VALUE self, VALUE arg1) { + return arg1; +} + +static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE arg1, VALUE arg2) { + return arg2; } static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) { @@ -155,25 +149,21 @@ void Init_module_spec(void) { rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2); rb_define_method(cls, "rb_define_const", module_specs_define_const, 3); rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2); - rb_define_method(cls, "rb_define_global_function", - module_specs_rb_define_global_function, 1); + rb_define_method(cls, "rb_define_global_function", module_specs_rb_define_global_function, 1); rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2); + rb_define_method(cls, "rb_define_method_varargs_1", module_specs_method_var_args_1, -1); + rb_define_method(cls, "rb_define_method_varargs_2", module_specs_method_var_args_2, -2); + rb_define_method(cls, "rb_define_method_1required", module_specs_rb_define_method_1required, 1); rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2); - rb_define_method(cls, "rb_define_method_c_array", module_specs_rb_define_method_c_array, 2); - rb_define_method(cls, "rb_define_method_ruby_array", module_specs_rb_define_method_ruby_array, 2); - rb_define_method(cls, "rb_define_module_function", - module_specs_rb_define_module_function, 2); + rb_define_method(cls, "rb_define_module_function", module_specs_rb_define_module_function, 2); - rb_define_method(cls, "rb_define_private_method", - module_specs_rb_define_private_method, 2); + rb_define_method(cls, "rb_define_private_method", module_specs_rb_define_private_method, 2); - rb_define_method(cls, "rb_define_protected_method", - module_specs_rb_define_protected_method, 2); + rb_define_method(cls, "rb_define_protected_method", module_specs_rb_define_protected_method, 2); - rb_define_method(cls, "rb_define_singleton_method", - module_specs_rb_define_singleton_method, 2); + rb_define_method(cls, "rb_define_singleton_method", module_specs_rb_define_singleton_method, 2); rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2); rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2); diff --git a/spec/ruby/optional/capi/ext/object_spec.c b/spec/ruby/optional/capi/ext/object_spec.c index 2670f24661..8aa98cc5ce 100644 --- a/spec/ruby/optional/capi/ext/object_spec.c +++ b/spec/ruby/optional/capi/ext/object_spec.c @@ -154,30 +154,12 @@ 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 +179,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,119 +196,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)) { 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; @@ -386,17 +371,21 @@ static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg) return rb_class_inherited_p(mod, arg); } +static int foreach_f(ID key, VALUE val, VALUE ary) { + rb_ary_push(ary, ID2SYM(key)); + rb_ary_push(ary, val); + return ST_CONTINUE; +} + +static VALUE object_spec_rb_ivar_foreach(VALUE self, VALUE obj) { + VALUE ary = rb_ary_new(); + rb_ivar_foreach(obj, foreach_f, ary); + return ary; +} + 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; } @@ -478,6 +467,7 @@ void Init_object_spec(void) { rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1); rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1); rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1); + rb_define_method(cls, "rb_is_rb_type_p_file", so_is_rb_type_p_file, 1); rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1); rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1); rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1); @@ -500,6 +490,7 @@ void Init_object_spec(void) { 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, "not_implemented_method", rb_f_notimplement, -1); + rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1); } #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..b0cf1a8662 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); diff --git a/spec/ruby/optional/capi/ext/rbasic_spec.c b/spec/ruby/optional/capi/ext/rbasic_spec.c index 9178e5f639..26be2fed6d 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); @@ -34,47 +42,53 @@ 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) { diff --git a/spec/ruby/optional/capi/ext/regexp_spec.c b/spec/ruby/optional/capi/ext/regexp_spec.c index 0a62616f33..9de7982b50 100644 --- a/spec/ruby/optional/capi/ext/regexp_spec.c +++ b/spec/ruby/optional/capi/ext/regexp_spec.c @@ -49,6 +49,12 @@ VALUE regexp_spec_match(VALUE self, VALUE regexp, VALUE str) { return rb_funcall(regexp, rb_intern("match"), 1, str); } +VALUE regexp_spec_memcicmp(VALUE self, VALUE str1, VALUE str2) { + long l1 = RSTRING_LEN(str1); + long l2 = RSTRING_LEN(str2); + return INT2FIX(rb_memcicmp(RSTRING_PTR(str1), RSTRING_PTR(str2), l1 < l2 ? l1 : l2)); +} + void Init_regexp_spec(void) { VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject); rb_define_method(cls, "match", regexp_spec_match, 2); @@ -60,6 +66,7 @@ void Init_regexp_spec(void) { rb_define_method(cls, "rb_reg_match_backref_get", regexp_spec_reg_match_backref_get, 2); rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1); rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1); + rb_define_method(cls, "rb_memcicmp", regexp_spec_memcicmp, 2); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h index 426b1ddc04..09135774af 100644 --- a/spec/ruby/optional/capi/ext/rubyspec.h +++ b/spec/ruby/optional/capi/ext/rubyspec.h @@ -11,6 +11,29 @@ # include <version.h> #endif +/* copied from ext/-test-/cxxanyargs/cxxanyargs.cpp */ +#if 0 /* Ignore deprecation warnings */ + +#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. + +#endif + #ifndef RUBY_VERSION_MAJOR #define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR #define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR @@ -21,47 +44,22 @@ ((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)) + +#if RUBY_VERSION_SINCE(3, 3, 0) +#define RUBY_VERSION_IS_3_3 +#endif -#if RUBY_VERSION_MAJOR > 3 || (RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 2) +#if RUBY_VERSION_SINCE(3, 2, 0) #define RUBY_VERSION_IS_3_2 #endif -#if RUBY_VERSION_MAJOR > 3 || (RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 1) +#if RUBY_VERSION_SINCE(3, 1, 0) #define RUBY_VERSION_IS_3_1 #endif -#if RUBY_VERSION_MAJOR > 3 || (RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 0) +#if RUBY_VERSION_SINCE(3, 0, 0) #define RUBY_VERSION_IS_3_0 #endif -#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 7) -#define RUBY_VERSION_IS_2_7 -#endif - -#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6) -#define RUBY_VERSION_IS_2_6 -#endif - -#if defined(__cplusplus) && !defined(RUBY_VERSION_IS_2_7) -/* Ruby < 2.7 needs this to let these function with callbacks and compile in C++ code */ -#define rb_define_method(mod, name, func, argc) rb_define_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_protected_method(mod, name, func, argc) rb_define_protected_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_private_method(mod, name, func, argc) rb_define_private_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_singleton_method(mod, name, func, argc) rb_define_singleton_method(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_module_function(mod, name, func, argc) rb_define_module_function(mod, name, RUBY_METHOD_FUNC(func), argc) -#define rb_define_global_function(name, func, argc) rb_define_global_function(name, RUBY_METHOD_FUNC(func), argc) -#define rb_hash_foreach(hash, func, farg) rb_hash_foreach(hash, (int (*)(...))func, farg) -#define st_foreach(tab, func, arg) st_foreach(tab, (int (*)(...))func, arg) -#define rb_block_call(object, name, args_count, args, block_call_func, data) rb_block_call(object, name, args_count, args, RUBY_METHOD_FUNC(block_call_func), data) -#define rb_ensure(b_proc, data1, e_proc, data2) rb_ensure(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2) -#define rb_rescue(b_proc, data1, e_proc, data2) rb_rescue(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2) -#define rb_rescue2(b_proc, data1, e_proc, data2, ...) rb_rescue2(RUBY_METHOD_FUNC(b_proc), data1, RUBY_METHOD_FUNC(e_proc), data2, __VA_ARGS__) -#define rb_catch(tag, func, data) rb_catch(tag, RUBY_METHOD_FUNC(func), data) -#define rb_catch_obj(tag, func, data) rb_catch_obj(tag, RUBY_METHOD_FUNC(func), data) -#define rb_proc_new(fn, arg) rb_proc_new(RUBY_METHOD_FUNC(fn), arg) -#define rb_fiber_new(fn, arg) rb_fiber_new(RUBY_METHOD_FUNC(fn), arg) -#define rb_thread_create(fn, arg) rb_thread_create(RUBY_METHOD_FUNC(fn), arg) -#define rb_define_hooked_variable(name, var, getter, setter) rb_define_hooked_variable(name, var, RUBY_METHOD_FUNC(getter), (void (*)(...))setter) -#endif - #endif diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c index b9a4a55853..cec3f65f45 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)); } @@ -129,7 +123,7 @@ VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) { 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 +133,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 +193,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 +209,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,16 +248,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)); @@ -275,14 +258,6 @@ VALUE string_spec_rb_tainted_str_new2(VALUE self, VALUE 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 +365,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); } @@ -437,6 +412,12 @@ VALUE string_spec_RSTRING_PTR_read(VALUE self, VALUE str, VALUE path) { return capacities; } +VALUE string_spec_RSTRING_PTR_null_terminate(VALUE self, VALUE str, VALUE min_length) { + char* ptr = RSTRING_PTR(str); + char* end = ptr + RSTRING_LEN(str); + return rb_str_new(end, FIX2LONG(min_length)); +} + VALUE string_spec_StringValue(VALUE self, VALUE str) { return StringValue(str); } @@ -567,7 +548,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); @@ -591,11 +572,19 @@ 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_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); @@ -662,6 +651,7 @@ void Init_string_spec(void) { rb_define_method(cls, "RSTRING_PTR_after_funcall", string_spec_RSTRING_PTR_after_funcall, 2); rb_define_method(cls, "RSTRING_PTR_after_yield", string_spec_RSTRING_PTR_after_yield, 1); rb_define_method(cls, "RSTRING_PTR_read", string_spec_RSTRING_PTR_read, 2); + rb_define_method(cls, "RSTRING_PTR_null_terminate", string_spec_RSTRING_PTR_null_terminate, 2); rb_define_method(cls, "StringValue", string_spec_StringValue, 1); rb_define_method(cls, "SafeStringValue", string_spec_SafeStringValue, 1); rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1); @@ -691,6 +681,8 @@ 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_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..9c45bd5672 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)); } @@ -56,14 +54,11 @@ 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); } diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c index be812d796f..3511c2fbcf 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]); 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..38889ecf5c 100644 --- a/spec/ruby/optional/capi/ext/typed_data_spec.c +++ b/spec/ruby/optional/capi/ext/typed_data_spec.c @@ -106,6 +106,12 @@ VALUE sws_typed_wrap_struct(VALUE self, VALUE val) { return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar); } +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 +171,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 +192,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 a7269353c2..b5bde420d2 100644 --- a/spec/ruby/optional/capi/ext/util_spec.c +++ b/spec/ruby/optional/capi/ext/util_spec.c @@ -7,15 +7,18 @@ extern "C" { #endif VALUE util_spec_rb_scan_args(VALUE self, VALUE argv, VALUE fmt, VALUE expected, VALUE acc) { - int i, result, argc = (int)RARRAY_LEN(argv); - VALUE args[6], failed, a1, a2, a3, a4, a5, a6; + int result, argc; + VALUE a1, a2, a3, a4, a5, a6; - failed = rb_intern("failed"); - a1 = a2 = a3 = a4 = a5 = a6 = failed; + argc = (int) RARRAY_LEN(argv); + VALUE* args = RARRAY_PTR(argv); + /* the line above can be replaced with this for Ruby implementations which do not support RARRAY_PTR() yet + VALUE args[6]; + for(int i = 0; i < argc; i++) { + args[i] = rb_ary_entry(argv, i); + } */ - for(i = 0; i < argc; i++) { - args[i] = rb_ary_entry(argv, i); - } + a1 = a2 = a3 = a4 = a5 = a6 = INT2FIX(-1); #ifdef RB_SCAN_ARGS_KEYWORDS if (*RSTRING_PTR(fmt) == 'k') { @@ -59,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/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/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/fixtures/object.rb b/spec/ruby/optional/capi/fixtures/object.rb new file mode 100644 index 0000000000..a59f2309d8 --- /dev/null +++ b/spec/ruby/optional/capi/fixtures/object.rb @@ -0,0 +1,29 @@ +class CApiObjectSpecs + class IVars + def initialize + @a = 3 + @b = 7 + @c = 4 + end + + def self.set_class_variables + @@foo = :a + @@bar = :b + @@baz = :c + end + end + + module MVars + @@mvar = :foo + @@mvar2 = :bar + + @ivar = :baz + end + + module CVars + @@cvar = :foo + @@cvar2 = :bar + + @ivar = :baz + end +end diff --git a/spec/ruby/optional/capi/gc_spec.rb b/spec/ruby/optional/capi/gc_spec.rb index 23e2b7c9ab..aaced56483 100644 --- a/spec/ruby/optional/capi/gc_spec.rb +++ b/spec/ruby/optional/capi/gc_spec.rb @@ -7,15 +7,60 @@ describe "CApiGCSpecs" do @f = CApiGCSpecs.new end - it "correctly gets the value from a registered address" do - @f.registered_tagged_address.should == 10 - @f.registered_tagged_address.should equal(@f.registered_tagged_address) - @f.registered_reference_address.should == "Globally registered data" - @f.registered_reference_address.should equal(@f.registered_reference_address) + describe "rb_gc_register_address" do + it "correctly gets the value from a registered address" do + @f.registered_tagged_address.should == 10 + @f.registered_tagged_address.should equal(@f.registered_tagged_address) + @f.registered_reference_address.should == "Globally registered data" + @f.registered_reference_address.should equal(@f.registered_reference_address) + end + + it "keeps the value alive even if the value is assigned after rb_gc_register_address() is called" do + GC.start + @f.registered_before_rb_gc_register_address.should == "registered before rb_gc_register_address()" + end + + it "can be called outside Init_" do + @f.rb_gc_register_address.should == "rb_gc_register_address() outside Init_" + @f.rb_gc_unregister_address + end end - describe "rb_gc_enable" do + describe "rb_global_variable" do + before :all do + GC.start + 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 + + describe "rb_gc_enable" do after do GC.enable end diff --git a/spec/ruby/optional/capi/globals_spec.rb b/spec/ruby/optional/capi/globals_spec.rb index cc6f6ef3a8..48677620bc 100644 --- a/spec/ruby/optional/capi/globals_spec.rb +++ b/spec/ruby/optional/capi/globals_spec.rb @@ -9,7 +9,7 @@ describe "CApiGlobalSpecs" do end it "correctly gets global values" do - @f.sb_gv_get("$BLAH").should == nil + suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil @f.sb_gv_get("$\\").should == nil @f.sb_gv_get("\\").should == nil # rb_gv_get should change \ to $\ end @@ -21,7 +21,7 @@ describe "CApiGlobalSpecs" do end it "correctly sets global values" do - @f.sb_gv_get("$BLAH").should == nil + suppress_warning { @f.sb_gv_get("$BLAH") }.should == nil @f.sb_gv_set("$BLAH", 10) begin @f.sb_gv_get("$BLAH").should == 10 @@ -42,6 +42,10 @@ describe "CApiGlobalSpecs" do end it "rb_define_readonly_variable should define a new readonly global variable" do + # 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 + @f.rb_define_readonly_variable("ro_gvar", 15) $ro_gvar.should == 15 -> { $ro_gvar = 10 }.should raise_error(NameError) @@ -53,6 +57,52 @@ describe "CApiGlobalSpecs" do $hooked_gvar.should == 4 end + it "rb_define_hooked_variable should use default accessors if NULL ones are supplied" do + @f.rb_define_hooked_variable_default_accessors("$hooked_gvar_default_accessors") + $hooked_gvar_default_accessors = 10 + $hooked_gvar_default_accessors.should == 10 + end + + it "rb_define_hooked_variable with default accessors should return nil for NULL variables" do + @f.rb_define_hooked_variable_null_var("$hooked_gvar_null_value") + $hooked_gvar_null_value.should == nil + end + + describe "rb_define_virtual_variable" do + describe "with default accessors" do + before :all do + @f.rb_define_virtual_variable_default_accessors("$virtual_variable_default_accessors") + end + + it "is read-only" do + -> { $virtual_variable_default_accessors = 10 }.should raise_error(NameError, /read-only/) + end + + it "returns false with the default getter" do + $virtual_variable_default_accessors.should == false + $virtual_variable_default_accessors.should == false + end + end + + describe "with supplied accessors" do + before :all do + @f.rb_define_virtual_variable_incrementing_accessors("$virtual_variable_incrementing_accessors") + end + + it "returns a dynamically changing value" do + $virtual_variable_incrementing_accessors = 20 + $virtual_variable_incrementing_accessors.should == 20 + $virtual_variable_incrementing_accessors.should == 21 + $virtual_variable_incrementing_accessors.should == 22 + + $virtual_variable_incrementing_accessors = 100 + $virtual_variable_incrementing_accessors.should == 100 + $virtual_variable_incrementing_accessors.should == 101 + $virtual_variable_incrementing_accessors.should == 102 + end + end + end + describe "rb_fs" do before :each do @field_separator = $; diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb index a60467a66b..a0e49ffc4c 100644 --- a/spec/ruby/optional/capi/hash_spec.rb +++ b/spec/ruby/optional/capi/hash_spec.rb @@ -50,6 +50,22 @@ describe "C-API Hash function" do end end + ruby_version_is '3.2' do + 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 + end + describe "rb_ident_hash_new" do it "returns a new compare by identity hash" do result = @s.rb_ident_hash_new diff --git a/spec/ruby/optional/capi/integer_spec.rb b/spec/ruby/optional/capi/integer_spec.rb index e26735824e..089872381c 100644 --- a/spec/ruby/optional/capi/integer_spec.rb +++ b/spec/ruby/optional/capi/integer_spec.rb @@ -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 489a01c515..870abef3ea 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -175,13 +175,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,12 +261,48 @@ describe "C-API IO function" do end end + ruby_version_is "3.1" do + 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 + 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 end end + describe "rb_thread_fd_select" do + it "waits until an fd is ready for reading" do + @w_io.write "rb_thread_fd_select" + @o.rb_thread_fd_select_read(@r_io).should == 1 + end + + it "waits until an fd is ready for writing" do + @o.rb_thread_fd_select_write(@w_io).should == 1 + end + + it "waits until an fd is ready for writing with timeout" do + @o.rb_thread_fd_select_timeout(@w_io).should == 1 + end + end + platform_is_not :windows do describe "rb_io_wait_readable" do it "returns false if there is no error condition" do @@ -290,6 +331,40 @@ describe "C-API IO function" do thr.join end end + + ruby_version_is "3.1" do + 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 "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 end describe "rb_thread_wait_fd" do @@ -329,10 +404,63 @@ describe "C-API IO function" do @o.rb_wait_for_single_fd(@r_io, 1, 0, 0).should == 0 end end + + ruby_version_is "3.1" do + 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 "returns false if there is no error condition" do + @o.rb_io_maybe_wait(0, @w_io, IO::WRITABLE, nil).should == false + 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 + 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 + 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 d1e3e03582..3b61d4f0f1 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -1,4 +1,5 @@ require_relative 'spec_helper' +require_relative 'fixtures/kernel' kernel_path = load_extension("kernel") @@ -502,6 +503,30 @@ describe "C-API Kernel function" do it "evaluates a string of ruby code" do @s.rb_eval_string("1+1").should == 2 end + + it "captures local variables when called within a method" do + a = 2 + @s.rb_eval_string("a+1").should == 3 + 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 + 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] + end end describe "rb_block_proc" do @@ -578,47 +603,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 - @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}], {}] + it "calls a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + @s.rb_funcallv(object, :protected_method, []).should == :protected + end + end + + 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 @@ -657,21 +700,91 @@ 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 end diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb index acf4d1fe48..d7c0ab9c52 100644 --- a/spec/ruby/optional/capi/module_spec.rb +++ b/spec/ruby/optional/capi/module_spec.rb @@ -252,22 +252,30 @@ describe "CApiModule" do cls.new.method(:test_method).arity.should == 0 end + it "returns the correct arity when argc of the method in class is 1" do + @m.rb_define_method_1required(42).should == 42 + @m.method(:rb_define_method_1required).arity.should == 1 + end + + it "returns the correct arity when argc of the method in class is 2" do + @m.rb_define_method_2required(1, 2).should == 2 + @m.method(:rb_define_method_2required).arity.should == 2 + end + + it "defines a method taking variable arguments as a C array if the argument count is -1" do + @m.rb_define_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4] + end + it "returns the correct arity when argc of the method in class is -1" do - cls = Class.new - @m.rb_define_method_c_array(cls, "test_method_c_array") - cls.new.method(:test_method_c_array).arity.should == -1 + @m.method(:rb_define_method_varargs_1).arity.should == -1 end - it "returns the correct arity when argc of the method in class is -2" do - cls = Class.new - @m.rb_define_method_ruby_array(cls, "test_method_ruby_array") - cls.new.method(:test_method_ruby_array).arity.should == -1 + it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do + @m.rb_define_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4] end - it "returns the correct arity when argc of the method in class is 2" do - cls = Class.new - @m.rb_define_method_2required(cls, "test_method_2required") - cls.new.method(:test_method_2required).arity.should == 2 + it "returns the correct arity when argc of the method in class is -2" do + @m.method(:rb_define_method_varargs_2).arity.should == -1 end it "defines a method on a module" do diff --git a/spec/ruby/optional/capi/object_spec.rb b/spec/ruby/optional/capi/object_spec.rb index 294247910b..7bc7bd992a 100644 --- a/spec/ruby/optional/capi/object_spec.rb +++ b/spec/ruby/optional/capi/object_spec.rb @@ -1,4 +1,5 @@ require_relative 'spec_helper' +require_relative 'fixtures/object' load_extension("object") @@ -30,6 +31,7 @@ describe "CApiObject" do class ObjectTest def initialize @foo = 7 + yield if block_given? end def foo @@ -88,6 +90,15 @@ describe "CApiObject" do o.initialized.should be_true o.arguments.should == [:one, :two] end + + it "passes the block to #initialize" do + v = nil + o = @o.rb_obj_alloc(ObjectTest) + @o.rb_obj_call_init(o, 0, []) do + v = :foo + end + v.should == :foo + end end describe "rb_is_instance_of" do @@ -208,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 @@ -513,6 +524,21 @@ describe "CApiObject" do @o.rb_is_type_class(ObjectTest).should == true @o.rb_is_type_data(Time.now).should == true end + + it "returns T_FILE for instances of IO and subclasses" do + STDERR.class.should == IO + @o.rb_is_rb_type_p_file(STDERR).should == true + + File.open(__FILE__) do |f| + f.class.should == File + @o.rb_is_rb_type_p_file(f).should == true + end + + require 'socket' + TCPServer.open(0) do |s| + @o.rb_is_rb_type_p_file(s).should == true + end + end end describe "rb_check_type" do @@ -660,7 +686,7 @@ 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 @@ -674,7 +700,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 @@ -868,9 +894,9 @@ describe "CApiObject" do 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 @@ -878,7 +904,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 == [] @@ -958,5 +984,30 @@ describe "CApiObject" do @o.speced_allocator?(parent).should == true end end + + describe "rb_ivar_foreach" do + it "calls the callback function for each instance variable on an object" do + o = CApiObjectSpecs::IVars.new + ary = @o.rb_ivar_foreach(o) + ary.should == [:@a, 3, :@b, 7, :@c, 4] + end + + it "calls the callback function for each cvar and ivar on a class" do + exp = [:@@cvar, :foo, :@@cvar2, :bar, :@ivar, :baz] + exp.unshift(:__classpath__, 'CApiObjectSpecs::CVars') if RUBY_VERSION < "3.3" + + ary = @o.rb_ivar_foreach(CApiObjectSpecs::CVars) + ary.should == exp + end + + it "calls the callback function for each cvar and ivar on a module" do + exp = [:@@mvar, :foo, :@@mvar2, :bar, :@ivar, :baz] + exp.unshift(:__classpath__, 'CApiObjectSpecs::MVars') if RUBY_VERSION < "3.3" + + ary = @o.rb_ivar_foreach(CApiObjectSpecs::MVars) + ary.should == exp + end + + end end end 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/rbasic_spec.rb b/spec/ruby/optional/capi/rbasic_spec.rb index 6300680d99..f3367e05ff 100644 --- a/spec/ruby/optional/capi/rbasic_spec.rb +++ b/spec/ruby/optional/capi/rbasic_spec.rb @@ -1,7 +1,6 @@ require_relative 'spec_helper' require_relative 'shared/rbasic' load_extension("rbasic") -return if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # not working from the beginning load_extension("data") load_extension("array") @@ -34,6 +33,8 @@ describe "RBasic support for RData" do 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 + @specs.copy_flags(obj2, obj1) @specs.get_flags(obj2).should == 1 << 14 | 1 << 16 | initial @specs.set_flags(obj1, initial) diff --git a/spec/ruby/optional/capi/regexp_spec.rb b/spec/ruby/optional/capi/regexp_spec.rb index 81844e2a6e..af366e17a2 100644 --- a/spec/ruby/optional/capi/regexp_spec.rb +++ b/spec/ruby/optional/capi/regexp_spec.rb @@ -109,4 +109,20 @@ describe "C-API Regexp function" do thr.join end end + + describe "rb_memicmp" do + it "returns 0 for identical strings" do + @p.rb_memcicmp('Hello', 'Hello').should == 0 + end + + it "returns 0 for strings which only differ in case" do + @p.rb_memcicmp('Hello', 'HELLO').should == 0 + @p.rb_memcicmp('HELLO', 'Hello').should == 0 + end + + it "returns the difference between the first non matching characters" do + @p.rb_memcicmp('Hello', 'HELLP').should == -1 + @p.rb_memcicmp('HELLp', 'Hello').should == 1 + end + end end diff --git a/spec/ruby/optional/capi/shared/rbasic.rb b/spec/ruby/optional/capi/shared/rbasic.rb index 99c2044bd7..9d80a93e1d 100644 --- a/spec/ruby/optional/capi/shared/rbasic.rb +++ b/spec/ruby/optional/capi/shared/rbasic.rb @@ -1,5 +1,4 @@ describe :rbasic, shared: true do - before :all do specs = CApiRBasicSpecs.new @taint = ruby_version_is(''...'3.1') ? specs.taint_flag : 0 @@ -10,7 +9,7 @@ describe :rbasic, shared: true do obj, _ = @data.call initial = @specs.get_flags(obj) obj.freeze - @specs.get_flags(obj).should == @freeze | initial + (@specs.get_flags(obj) & 0xFFFF).should == (@freeze | initial) & 0xFFFF end it "supports setting the FREEZE flag" do diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb index ec6b909397..2691aa1332 100644 --- a/spec/ruby/optional/capi/spec_helper.rb +++ b/spec/ruby/optional/capi/spec_helper.rb @@ -34,7 +34,8 @@ def compile_extension(name) abi_header = "#{rubyhdrdir}/ruby/internal/abi.h" if RbConfig::CONFIG["ENABLE_SHARED"] == "yes" - libdirname = RbConfig::CONFIG['libdirname'] # defined since 2.1 + # below is defined since 2.1, except for mswin, and maybe other platforms + libdirname = RbConfig::CONFIG.fetch 'libdirname', 'libdir' libruby = "#{RbConfig::CONFIG[libdirname]}/#{RbConfig::CONFIG['LIBRUBY']}" end diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 7ad4d10ee4..a8edf998b5 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 @@ -97,6 +98,32 @@ describe "C-API String function" do end end + describe "rb_str_set_len on a UTF-16 String" do + before :each do + @str = "abcdefghij".dup.force_encoding(Encoding::UTF_16BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts two NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 8).b.should == "abcd\x00\x00gh".b + end + end + + describe "rb_str_set_len on a UTF-32 String" do + before :each do + @str = "abcdefghijkl".dup.force_encoding(Encoding::UTF_32BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts four NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 12).b.should == "abcd\x00\x00\x00\x00ijkl".b + end + end + describe "rb_str_buf_new" do it "returns the equivalent of an empty string" do buf = @s.rb_str_buf_new(10, nil) @@ -205,7 +232,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 @@ -221,14 +248,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 @@ -392,7 +419,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 @@ -472,29 +499,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 @@ -592,6 +600,12 @@ describe "C-API String function" do capacities[0].should < capacities[1] str.should == "fixture file contents to test read() with RSTRING_PTR" end + + it "terminates the string with at least (encoding min length) \\0 bytes" do + @s.RSTRING_PTR_null_terminate("abc", 1).should == "\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-16BE"), 2).should == "\x00\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-32BE"), 4).should == "\x00\x00\x00\x00" + end end describe "RSTRING_LEN" do @@ -699,7 +713,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 @@ -830,11 +844,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) @@ -854,7 +868,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 @@ -862,14 +876,14 @@ 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 @@ -879,7 +893,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(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") @@ -888,7 +902,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(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a) end @@ -898,7 +912,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 @@ -906,7 +920,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 @@ -917,7 +931,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") @@ -926,7 +940,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 @@ -938,7 +952,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 @@ -956,7 +970,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 @@ -1008,9 +1022,11 @@ end @s.rb_sprintf3(true.class).should == s end - it "formats a TrueClass VALUE as 'true' if sign specified in format" do - s = 'Result: true.' - @s.rb_sprintf4(true.class).should == s + 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 end it "truncates a string to a supplied precision if that is shorter than the string" do @@ -1193,4 +1209,67 @@ end -> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string') 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_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/thread_spec.rb b/spec/ruby/optional/capi/thread_spec.rb index 5cb46bbb7c..af641f0564 100644 --- a/spec/ruby/optional/capi/thread_spec.rb +++ b/spec/ruby/optional/capi/thread_spec.rb @@ -165,25 +165,23 @@ 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 + # Make sure it stopped and we got a proper value + thr.value.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 64b0894087..9ff8b4760a 100644 --- a/spec/ruby/optional/capi/util_spec.rb +++ b/spec/ruby/optional/capi/util_spec.rb @@ -15,8 +15,9 @@ describe "C-API Util function" do end it "assigns the required arguments scanned" do - @o.rb_scan_args([1, 2], "2", 2, @acc).should == 2 - ScratchPad.recorded.should == [1, 2] + obj = Object.new + @o.rb_scan_args([obj, 2], "2", 2, @acc).should == 2 + ScratchPad.recorded.should == [obj, 2] end it "raises an ArgumentError if there are insufficient arguments" do @@ -47,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 @@ -114,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 @@ -137,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 |