This commit is contained in:
Andrew Konchin 2024-05-09 20:35:01 +03:00 committed by KJ Tsanaktsidis
parent dbbaf871de
commit ad636033e2
31 changed files with 425 additions and 25 deletions

View File

@ -10,4 +10,21 @@ describe "Binding#dup" do
bind.frozen?.should == true bind.frozen?.should == true
bind.dup.frozen?.should == false bind.dup.frozen?.should == false
end end
it "retains original binding variables but the list is distinct" do
bind1 = binding
eval "a = 1", bind1
bind2 = bind1.dup
eval("a = 2", bind2)
eval("a", bind1).should == 2
eval("a", bind2).should == 2
eval("b = 2", bind2)
-> { eval("b", bind1) }.should raise_error(NameError)
eval("b", bind2).should == 2
bind1.local_variables.sort.should == [:a, :bind1, :bind2]
bind2.local_variables.sort.should == [:a, :b, :bind1, :bind2]
end
end end

View File

@ -11,6 +11,7 @@ describe "Enumerator#next_values" do
yield :e1, :e2, :e3 yield :e1, :e2, :e3
yield nil yield nil
yield yield
yield [:f1, :f2]
end end
@e = o.to_enum @e = o.to_enum
@ -48,8 +49,13 @@ describe "Enumerator#next_values" do
@e.next_values.should == [] @e.next_values.should == []
end end
it "raises StopIteration if called on a finished enumerator" do it "returns an array of array if yield is called with an array" do
7.times { @e.next } 7.times { @e.next }
@e.next_values.should == [[:f1, :f2]]
end
it "raises StopIteration if called on a finished enumerator" do
8.times { @e.next }
-> { @e.next_values }.should raise_error(StopIteration) -> { @e.next_values }.should raise_error(StopIteration)
end end
end end

View File

@ -11,6 +11,7 @@ describe "Enumerator#peek_values" do
yield :e1, :e2, :e3 yield :e1, :e2, :e3
yield nil yield nil
yield yield
yield [:f1, :f2]
end end
@e = o.to_enum @e = o.to_enum
@ -50,8 +51,13 @@ describe "Enumerator#peek_values" do
@e.peek_values.should == [] @e.peek_values.should == []
end end
it "raises StopIteration if called on a finished enumerator" do it "returns an array of array if yield is called with an array" do
7.times { @e.next } 7.times { @e.next }
@e.peek_values.should == [[:f1, :f2]]
end
it "raises StopIteration if called on a finished enumerator" do
8.times { @e.next }
-> { @e.peek_values }.should raise_error(StopIteration) -> { @e.peek_values }.should raise_error(StopIteration)
end end
end end

View File

@ -22,7 +22,7 @@ guard -> { platform_is_not :windows or ruby_version_is "3.3" } do
it "accepts a length, an offset, and an output buffer" do it "accepts a length, an offset, and an output buffer" do
buffer = +"foo" buffer = +"foo"
@file.pread(3, 4, buffer) @file.pread(3, 4, buffer).should.equal?(buffer)
buffer.should == "567" buffer.should == "567"
end end
@ -38,6 +38,13 @@ guard -> { platform_is_not :windows or ruby_version_is "3.3" } do
buffer.should == "12345" buffer.should == "12345"
end end
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@file.pread(10, 0, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
it "does not advance the file pointer" do it "does not advance the file pointer" do
@file.pread(4, 0).should == "1234" @file.pread(4, 0).should == "1234"
@file.read.should == "1234567890" @file.read.should == "1234567890"

View File

@ -376,6 +376,21 @@ describe "IO#read" do
buf.should == @contents[0..4] buf.should == @contents[0..4]
end end
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.read(10, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
# https://bugs.ruby-lang.org/issues/20416
it "does not preserve the encoding of the given buffer when max length is not provided" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.read(nil, buffer)
buffer.encoding.should_not == Encoding::ISO_8859_1
end
it "returns the given buffer" do it "returns the given buffer" do
buf = +"" buf = +""

View File

@ -62,7 +62,7 @@ describe "IO#readpartial" do
buffer = +"existing content" buffer = +"existing content"
@wr.write("hello world") @wr.write("hello world")
@wr.close @wr.close
@rd.readpartial(11, buffer) @rd.readpartial(11, buffer).should.equal?(buffer)
buffer.should == "hello world" buffer.should == "hello world"
end end
@ -106,6 +106,7 @@ describe "IO#readpartial" do
@wr.write("abc") @wr.write("abc")
@wr.close @wr.close
@rd.readpartial(10, buffer) @rd.readpartial(10, buffer)
buffer.encoding.should == Encoding::ISO_8859_1 buffer.encoding.should == Encoding::ISO_8859_1
end end
end end

View File

@ -97,7 +97,7 @@ describe "IO#sysread on a file" do
it "discards the existing buffer content upon successful read" do it "discards the existing buffer content upon successful read" do
buffer = +"existing content" buffer = +"existing content"
@file.sysread(11, buffer) @file.sysread(11, buffer).should.equal?(buffer)
buffer.should == "01234567890" buffer.should == "01234567890"
end end
@ -107,6 +107,13 @@ describe "IO#sysread on a file" do
-> { @file.sysread(1, buffer) }.should raise_error(EOFError) -> { @file.sysread(1, buffer) }.should raise_error(EOFError)
buffer.should be_empty buffer.should be_empty
end end
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
string = @file.sysread(10, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
end end
describe "IO#sysread" do describe "IO#sysread" do

View File

@ -47,6 +47,34 @@ describe "Module#include" do
-> { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) -> { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
end end
ruby_version_is ""..."3.2" do
it "raises ArgumentError when the argument is a refinement" do
refinement = nil
Module.new do
refine String do
refinement = self
end
end
-> { ModuleSpecs::Basic.include(refinement) }.should raise_error(ArgumentError, "refinement module is not allowed")
end
end
ruby_version_is "3.2" do
it "raises a TypeError when the argument is a refinement" do
refinement = nil
Module.new do
refine String do
refinement = self
end
end
-> { ModuleSpecs::Basic.include(refinement) }.should raise_error(TypeError, "Cannot include refinement")
end
end
it "imports constants to modules and classes" do it "imports constants to modules and classes" do
ModuleSpecs::A.constants.should include(:CONSTANT_A) ModuleSpecs::A.constants.should include(:CONSTANT_A)
ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B) ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B)

View File

@ -75,6 +75,26 @@ describe "Module#prepend" do
foo.call.should == 'm' foo.call.should == 'm'
end end
it "updates the optimized method when a prepended module is updated" do
out = ruby_exe(<<~RUBY)
module M; end
class Integer
prepend M
end
l = -> { 1 + 2 }
p l.call
M.module_eval do
def +(o)
$called = true
super(o)
end
end
p l.call
p $called
RUBY
out.should == "3\n3\ntrue\n"
end
it "updates the method when there is a base included method and the prepended module overrides it" do it "updates the method when there is a base included method and the prepended module overrides it" do
base_module = Module.new do base_module = Module.new do
def foo def foo
@ -415,6 +435,34 @@ describe "Module#prepend" do
-> { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) -> { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
end end
ruby_version_is ""..."3.2" do
it "raises ArgumentError when the argument is a refinement" do
refinement = nil
Module.new do
refine String do
refinement = self
end
end
-> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(ArgumentError, "refinement module is not allowed")
end
end
ruby_version_is "3.2" do
it "raises a TypeError when the argument is a refinement" do
refinement = nil
Module.new do
refine String do
refinement = self
end
end
-> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(TypeError, "Cannot prepend refinement")
end
end
it "imports constants" do it "imports constants" do
m1 = Module.new m1 = Module.new
m1::MY_CONSTANT = 1 m1::MY_CONSTANT = 1

View File

@ -0,0 +1,28 @@
require_relative '../../spec_helper'
describe "Performance warnings" do
guard -> { ruby_version_is("3.4") || RUBY_ENGINE == "truffleruby" } do
# Optimising Integer, Float or Symbol methods is kind of implementation detail
# but multiple implementations do so. So it seems reasonable to have a test case
# for at least one such common method.
# See https://bugs.ruby-lang.org/issues/20429
context "when redefined optimised methods" do
it "emits performance warning for redefining Integer#+" do
code = <<~CODE
Warning[:performance] = true
class Integer
ORIG_METHOD = instance_method(:+)
def +(...)
ORIG_METHOD.bind(self).call(...)
end
end
CODE
ruby_exe(code, args: "2>&1").should.include?("warning: Redefining 'Integer#+' disables interpreter and JIT optimizations")
end
end
end
end

View File

@ -252,6 +252,25 @@ describe "Break inside a while loop" do
end end
end end
describe "The break statement in a method" do
it "is invalid and raises a SyntaxError" do
-> {
eval("def m; break; end")
}.should raise_error(SyntaxError)
end
end
describe "The break statement in a module literal" do
it "is invalid and raises a SyntaxError" do
code = <<~RUBY
module BreakSpecs:ModuleWithBreak
break
end
RUBY
-> { eval(code) }.should raise_error(SyntaxError)
end
end
# TODO: Rewrite all the specs from here to the end of the file in the style # TODO: Rewrite all the specs from here to the end of the file in the style
# above. # above.

View File

@ -38,7 +38,7 @@ describe "``" do
2.times do 2.times do
runner.instance_exec do runner.instance_exec do
`test #{:command}` `test #{:command}` # rubocop:disable Lint/LiteralInInterpolation
end end
end end
@ -84,7 +84,7 @@ describe "%x" do
2.times do 2.times do
runner.instance_exec do runner.instance_exec do
%x{test #{:command}} %x{test #{:command}} # rubocop:disable Lint/LiteralInInterpolation
end end
end end

View File

@ -86,6 +86,30 @@ describe "Hash literal" do
-> { eval("{:a ==> 1}") }.should raise_error(SyntaxError) -> { eval("{:a ==> 1}") }.should raise_error(SyntaxError)
end end
it "recognizes '!' at the end of the key" do
eval("{:a! =>1}").should == {:"a!" => 1}
eval("{:a! => 1}").should == {:"a!" => 1}
eval("{a!:1}").should == {:"a!" => 1}
eval("{a!: 1}").should == {:"a!" => 1}
end
it "raises a SyntaxError if there is no space between `!` and `=>`" do
-> { eval("{:a!=> 1}") }.should raise_error(SyntaxError)
end
it "recognizes '?' at the end of the key" do
eval("{:a? =>1}").should == {:"a?" => 1}
eval("{:a? => 1}").should == {:"a?" => 1}
eval("{a?:1}").should == {:"a?" => 1}
eval("{a?: 1}").should == {:"a?" => 1}
end
it "raises a SyntaxError if there is no space between `?` and `=>`" do
-> { eval("{:a?=> 1}") }.should raise_error(SyntaxError)
end
it "constructs a new hash with the given elements" do it "constructs a new hash with the given elements" do
{foo: 123}.should == {foo: 123} {foo: 123}.should == {foo: 123}
h = {rbx: :cool, specs: 'fail_sometimes'} h = {rbx: :cool, specs: 'fail_sometimes'}
@ -271,6 +295,14 @@ describe "The ** operator" do
a.new.foo(1).should == {bar: "baz", val: 1} a.new.foo(1).should == {bar: "baz", val: 1}
end end
it "raises a SyntaxError when the hash key ends with `!`" do
-> { eval("{a!:}") }.should raise_error(SyntaxError, /identifier a! is not valid to get/)
end
it "raises a SyntaxError when the hash key ends with `?`" do
-> { eval("{a?:}") }.should raise_error(SyntaxError, /identifier a\? is not valid to get/)
end
end end
end end
end end

View File

@ -395,4 +395,32 @@ describe "Keyword arguments" do
end end
end end
end end
context "in define_method(name, &proc)" do
# This tests that a free-standing proc used in define_method and converted to ruby2_keywords adopts that logic.
# See jruby/jruby#8119 for a case where aggressive JIT optimization broke later ruby2_keywords changes.
it "works with ruby2_keywords" do
m = Class.new do
def bar(a, foo: nil)
[a, foo]
end
# define_method and ruby2_keywords using send to avoid peephole optimizations
def self.setup
pr = make_proc
send :define_method, :foo, &pr
send :ruby2_keywords, :foo
end
# create proc in isolated method to force jit compilation on some implementations
def self.make_proc
proc { |a, *args| bar(a, *args) }
end
end
m.setup
m.new.foo(1, foo:2).should == [1, 2]
end
end
end end

View File

@ -22,6 +22,15 @@ describe "Regexps with back-references" do
$10.should == "0" $10.should == "0"
end end
it "returns nil for numbered variable with too large index" do
-> {
eval(<<~CODE).should == nil
"a" =~ /(.)/
eval('$4294967296')
CODE
}.should complain(/warning: ('|`)\$4294967296' is too big for a number variable, always nil/)
end
it "will not clobber capture variables across threads" do it "will not clobber capture variables across threads" do
cap1, cap2, cap3 = nil cap1, cap2, cap3 = nil
"foo" =~ /(o+)/ "foo" =~ /(o+)/

View File

@ -31,8 +31,11 @@ describe "The retry statement" do
results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5] results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5]
end end
it "raises a SyntaxError when used outside of a begin statement" do it "raises a SyntaxError when used outside of a rescue statement" do
-> { eval 'retry' }.should raise_error(SyntaxError) -> { eval 'retry' }.should raise_error(SyntaxError)
-> { eval 'begin; retry; end' }.should raise_error(SyntaxError)
-> { eval 'def m; retry; end' }.should raise_error(SyntaxError)
-> { eval 'module RetrySpecs; retry; end' }.should raise_error(SyntaxError)
end end
end end

View File

@ -206,3 +206,15 @@ describe "Using yield in non-lambda block" do
-> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/) -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
end end
end end
describe "Using yield in a module literal" do
it 'raises a SyntaxError' do
code = <<~RUBY
module YieldSpecs::ModuleWithYield
yield
end
RUBY
-> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
end
end

View File

@ -1,5 +1,5 @@
require_relative '../../spec_helper' require_relative '../../../spec_helper'
require 'time' require 'time'
describe "Time#to_date" do describe "Time#to_date" do

View File

@ -1,4 +1,4 @@
require_relative '../../spec_helper' require_relative '../../../spec_helper'
require 'time' require 'time'
require 'date' require 'date'
date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0' date_version = defined?(Date::VERSION) ? Date::VERSION : '3.1.0'

View File

@ -52,9 +52,19 @@ describe "Socket::BasicSocket#recv_nonblock" do
@s2.send("data", 0, @s1.getsockname) @s2.send("data", 0, @s1.getsockname)
IO.select([@s1], nil, nil, 2) IO.select([@s1], nil, nil, 2)
buf = +"foo" buffer = +"foo"
@s1.recv_nonblock(5, 0, buf) @s1.recv_nonblock(5, 0, buffer).should.equal?(buffer)
buf.should == "data" buffer.should == "data"
end
it "preserves the encoding of the given buffer" do
@s1.bind(Socket.pack_sockaddr_in(0, ip_address))
@s2.send("data", 0, @s1.getsockname)
IO.select([@s1], nil, nil, 2)
buffer = ''.encode(Encoding::ISO_8859_1)
@s1.recv_nonblock(5, 0, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end end
it "does not block if there's no data available" do it "does not block if there's no data available" do

View File

@ -100,13 +100,29 @@ describe "BasicSocket#recv" do
socket.write("data") socket.write("data")
client = @server.accept client = @server.accept
buf = +"foo" buffer = +"foo"
begin begin
client.recv(4, 0, buf) client.recv(4, 0, buffer).should.equal?(buffer)
ensure ensure
client.close client.close
end end
buf.should == "data" buffer.should == "data"
socket.close
end
it "preserves the encoding of the given buffer" do
socket = TCPSocket.new('127.0.0.1', @port)
socket.write("data")
client = @server.accept
buffer = ''.encode(Encoding::ISO_8859_1)
begin
client.recv(4, 0, buffer)
ensure
client.close
end
buffer.encoding.should == Encoding::ISO_8859_1
socket.close socket.close
end end

View File

@ -52,6 +52,27 @@ describe 'Socket#recvfrom_nonblock' do
end end
end end
it "allows an output buffer as third argument" do
@client.write('hello')
IO.select([@server])
buffer = +''
message, = @server.recvfrom_nonblock(5, 0, buffer)
message.should.equal?(buffer)
buffer.should == 'hello'
end
it "preserves the encoding of the given buffer" do
@client.write('hello')
IO.select([@server])
buffer = ''.encode(Encoding::ISO_8859_1)
@server.recvfrom_nonblock(5, 0, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
describe 'the returned data' do describe 'the returned data' do
it 'is the same as the sent data' do it 'is the same as the sent data' do
5.times do 5.times do

View File

@ -58,6 +58,15 @@ describe 'UDPSocket#recvfrom_nonblock' do
buffer.should == 'h' buffer.should == 'h'
end end
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
IO.select([@server])
message, = @server.recvfrom_nonblock(1, 0, buffer)
message.should.equal?(buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
describe 'the returned Array' do describe 'the returned Array' do
before do before do
IO.select([@server]) IO.select([@server])

View File

@ -31,6 +31,29 @@ with_feature :unix_socket do
sock.close sock.close
end end
it "allows an output buffer as third argument" do
buffer = +''
@client.send("foobar", 0)
sock = @server.accept
message, = sock.recvfrom(6, 0, buffer)
sock.close
message.should.equal?(buffer)
buffer.should == "foobar"
end
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@client.send("foobar", 0)
sock = @server.accept
sock.recvfrom(6, 0, buffer)
sock.close
buffer.encoding.should == Encoding::ISO_8859_1
end
it "uses different message options" do it "uses different message options" do
@client.send("foobar", Socket::MSG_PEEK) @client.send("foobar", Socket::MSG_PEEK)
sock = @server.accept sock = @server.accept

View File

@ -11,10 +11,28 @@ describe :stringio_read, shared: true do
end end
it "reads length bytes and writes them to the buffer String" do it "reads length bytes and writes them to the buffer String" do
@io.send(@method, 7, buffer = +"") @io.send(@method, 7, buffer = +"").should.equal?(buffer)
buffer.should == "example" buffer.should == "example"
end end
ruby_version_is ""..."3.4" do
it "does not preserve the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.send(@method, 7, buffer)
buffer.encoding.should_not == Encoding::ISO_8859_1
end
end
ruby_version_is "3.4" do
it "preserves the encoding of the given buffer" do
buffer = ''.encode(Encoding::ISO_8859_1)
@io.send(@method, 7, buffer)
buffer.encoding.should == Encoding::ISO_8859_1
end
end
it "tries to convert the passed buffer Object to a String using #to_str" do it "tries to convert the passed buffer Object to a String using #to_str" do
obj = mock("to_str") obj = mock("to_str")
obj.should_receive(:to_str).and_return(buffer = +"") obj.should_receive(:to_str).and_return(buffer = +"")

View File

@ -487,4 +487,16 @@ describe "C-API Class function" do
@s.rb_class_real(0).should == 0 @s.rb_class_real(0).should == 0
end end
end end
describe "rb_class_get_superclass" do
it "returns parent class for a provided class" do
a = Class.new
@s.rb_class_get_superclass(Class.new(a)).should == a
end
it "returns false when there is no parent class" do
@s.rb_class_get_superclass(BasicObject).should == false
@s.rb_class_get_superclass(Module.new).should == false
end
end
end end

View File

@ -79,6 +79,10 @@ static VALUE class_spec_rb_class_real(VALUE self, VALUE object) {
} }
} }
static VALUE class_spec_rb_class_get_superclass(VALUE self, VALUE klass) {
return rb_class_get_superclass(klass);
}
static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) { static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) {
return rb_class_superclass(klass); return rb_class_superclass(klass);
} }
@ -160,6 +164,7 @@ void Init_class_spec(void) {
rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2); rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2);
#endif #endif
rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1); rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1);
rb_define_method(cls, "rb_class_get_superclass", class_spec_rb_class_get_superclass, 1);
rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1); rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1);
rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2); rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2);
rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2); rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2);

View File

@ -16,6 +16,8 @@ VALUE registered_after_rb_global_variable_bignum;
VALUE registered_after_rb_global_variable_float; VALUE registered_after_rb_global_variable_float;
VALUE rb_gc_register_address_outside_init; VALUE rb_gc_register_address_outside_init;
VALUE rb_gc_register_mark_object_not_referenced_float;
static VALUE registered_tagged_address(VALUE self) { static VALUE registered_tagged_address(VALUE self) {
return registered_tagged_value; return registered_tagged_value;
} }
@ -90,6 +92,10 @@ static VALUE gc_spec_rb_gc_register_mark_object(VALUE self, VALUE obj) {
return Qnil; return Qnil;
} }
static VALUE gc_spec_rb_gc_register_mark_object_not_referenced_float(VALUE self) {
return rb_gc_register_mark_object_not_referenced_float;
}
void Init_gc_spec(void) { void Init_gc_spec(void) {
VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject); VALUE cls = rb_define_class("CApiGCSpecs", rb_cObject);
@ -115,6 +121,9 @@ void Init_gc_spec(void) {
registered_after_rb_global_variable_float = DBL2NUM(6.28); registered_after_rb_global_variable_float = DBL2NUM(6.28);
rb_global_variable(&registered_after_rb_global_variable_float); rb_global_variable(&registered_after_rb_global_variable_float);
rb_gc_register_mark_object_not_referenced_float = DBL2NUM(1.61);
rb_gc_register_mark_object(rb_gc_register_mark_object_not_referenced_float);
rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0); rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0);
rb_define_method(cls, "registered_reference_address", registered_reference_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_gc_register_address", get_registered_before_rb_gc_register_address, 0);
@ -131,6 +140,7 @@ void Init_gc_spec(void) {
rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0); rb_define_method(cls, "rb_gc", gc_spec_rb_gc, 0);
rb_define_method(cls, "rb_gc_adjust_memory_usage", gc_spec_rb_gc_adjust_memory_usage, 1); rb_define_method(cls, "rb_gc_adjust_memory_usage", gc_spec_rb_gc_adjust_memory_usage, 1);
rb_define_method(cls, "rb_gc_register_mark_object", gc_spec_rb_gc_register_mark_object, 1); rb_define_method(cls, "rb_gc_register_mark_object", gc_spec_rb_gc_register_mark_object, 1);
rb_define_method(cls, "rb_gc_register_mark_object_not_referenced_float", gc_spec_rb_gc_register_mark_object_not_referenced_float, 0);
rb_define_method(cls, "rb_gc_latest_gc_info", gc_spec_rb_gc_latest_gc_info, 1); rb_define_method(cls, "rb_gc_latest_gc_info", gc_spec_rb_gc_latest_gc_info, 1);
} }

View File

@ -390,22 +390,22 @@ static VALUE speced_allocator(VALUE klass) {
return instance; return instance;
} }
static VALUE define_alloc_func(VALUE self, VALUE klass) { static VALUE object_spec_rb_define_alloc_func(VALUE self, VALUE klass) {
rb_define_alloc_func(klass, speced_allocator); rb_define_alloc_func(klass, speced_allocator);
return Qnil; return Qnil;
} }
static VALUE undef_alloc_func(VALUE self, VALUE klass) { static VALUE object_spec_rb_undef_alloc_func(VALUE self, VALUE klass) {
rb_undef_alloc_func(klass); rb_undef_alloc_func(klass);
return Qnil; return Qnil;
} }
static VALUE speced_allocator_p(VALUE self, VALUE klass) { static VALUE object_spec_speced_allocator_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass); rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return (allocator == speced_allocator) ? Qtrue : Qfalse; return (allocator == speced_allocator) ? Qtrue : Qfalse;
} }
static VALUE custom_alloc_func_p(VALUE self, VALUE klass) { static VALUE object_spec_custom_alloc_func_p(VALUE self, VALUE klass) {
rb_alloc_func_t allocator = rb_get_alloc_func(klass); rb_alloc_func_t allocator = rb_get_alloc_func(klass);
return allocator ? Qtrue : Qfalse; return allocator ? Qtrue : Qfalse;
} }
@ -485,10 +485,10 @@ void Init_object_spec(void) {
rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2); rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2);
rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2); rb_define_method(cls, "rb_copy_generic_ivar", object_spec_rb_copy_generic_ivar, 2);
rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1); rb_define_method(cls, "rb_free_generic_ivar", object_spec_rb_free_generic_ivar, 1);
rb_define_method(cls, "rb_define_alloc_func", define_alloc_func, 1); rb_define_method(cls, "rb_define_alloc_func", object_spec_rb_define_alloc_func, 1);
rb_define_method(cls, "rb_undef_alloc_func", undef_alloc_func, 1); rb_define_method(cls, "rb_undef_alloc_func", object_spec_rb_undef_alloc_func, 1);
rb_define_method(cls, "speced_allocator?", speced_allocator_p, 1); rb_define_method(cls, "speced_allocator?", object_spec_speced_allocator_p, 1);
rb_define_method(cls, "custom_alloc_func?", custom_alloc_func_p, 1); rb_define_method(cls, "custom_alloc_func?", object_spec_custom_alloc_func_p, 1);
rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1); rb_define_method(cls, "not_implemented_method", rb_f_notimplement, -1);
rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1); rb_define_method(cls, "rb_ivar_foreach", object_spec_rb_ivar_foreach, 1);
} }

View File

@ -108,6 +108,10 @@ describe "CApiGCSpecs" do
it "can be called with an object" do it "can be called with an object" do
@f.rb_gc_register_mark_object(Object.new).should be_nil @f.rb_gc_register_mark_object(Object.new).should be_nil
end end
it "keeps the value alive even if the value is not referenced by any Ruby object" do
@f.rb_gc_register_mark_object_not_referenced_float.should == 1.61
end
end end
describe "rb_gc_latest_gc_info" do describe "rb_gc_latest_gc_info" do

View File

@ -30,6 +30,12 @@ describe :kernel_at_exit, shared: true do
result.lines.should.include?("The exception matches: true (message=foo)\n") result.lines.should.include?("The exception matches: true (message=foo)\n")
end end
it "gives access to an exception raised in a previous handler" do
code = "#{@method} { print '$!.message = ' + $!.message }; #{@method} { raise 'foo' }"
result = ruby_exe(code, args: "2>&1", exit_status: 1)
result.lines.should.include?("$!.message = foo")
end
it "both exceptions in a handler and in the main script are printed" do it "both exceptions in a handler and in the main script are printed" do
code = "#{@method} { raise 'at_exit_error' }; raise 'main_script_error'" code = "#{@method} { raise 'at_exit_error' }; raise 'main_script_error'"
result = ruby_exe(code, args: "2>&1", exit_status: 1) result = ruby_exe(code, args: "2>&1", exit_status: 1)