diff --git a/CHANGELOG.md b/CHANGELOG.md index 6032f41ce522..69e8776ad92f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Compatibility: * Warn when a global variable is not initialized (#2595, @andrykonchin). * Fix escaping of `/` by `Regexp#source` (#2569, @andrykonchin). * Range literals of integers are now created at parse time like in CRuby (#2622, @aardvark179). +* Fix `IO.pipe` - allow overriding `IO.new` that is used to create new pipes (#2692, @andykonchin). Performance: diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb index 067ab59d9301..204a2a101b00 100644 --- a/spec/ruby/core/io/fixtures/classes.rb +++ b/spec/ruby/core/io/fixtures/classes.rb @@ -7,6 +7,18 @@ module IOSpecs class SubIO < IO end + class SubIOWithRedefinedNew < IO + def self.new(...) + ScratchPad << :redefined_new_called + super + end + + def initialize(...) + ScratchPad << :call_original_initialize + super + end + end + def self.collector Proc.new { |x| ScratchPad << x } end diff --git a/spec/ruby/core/io/pipe_spec.rb b/spec/ruby/core/io/pipe_spec.rb index 2f2cf06f4d32..aee0d9003f4e 100644 --- a/spec/ruby/core/io/pipe_spec.rb +++ b/spec/ruby/core/io/pipe_spec.rb @@ -25,6 +25,17 @@ @r.should be_an_instance_of(IOSpecs::SubIO) @w.should be_an_instance_of(IOSpecs::SubIO) end + + it "does not use IO.new method to create pipes and allows its overriding" do + ScratchPad.record [] + + # so redefined .new is not called, but original #initialize is + @r, @w = IOSpecs::SubIOWithRedefinedNew.pipe + ScratchPad.recorded.should == [:call_original_initialize, :call_original_initialize] # called 2 times - for each pipe (r and w) + + @r.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew) + @w.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew) + end end describe "IO.pipe" do diff --git a/src/main/java/org/truffleruby/core/klass/ClassNodes.java b/src/main/java/org/truffleruby/core/klass/ClassNodes.java index 61029eb8fead..b044af1d9d92 100644 --- a/src/main/java/org/truffleruby/core/klass/ClassNodes.java +++ b/src/main/java/org/truffleruby/core/klass/ClassNodes.java @@ -283,7 +283,6 @@ protected RubyClass newSingletonInstance( } } - @CoreMethod(names = "initialize", optional = 1) public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode { @Specialization diff --git a/src/main/ruby/truffleruby/core/truffle/io_operations.rb b/src/main/ruby/truffleruby/core/truffle/io_operations.rb index ce3187969131..de961c6a708b 100644 --- a/src/main/ruby/truffleruby/core/truffle/io_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/io_operations.rb @@ -99,6 +99,8 @@ def self.pipe_end_setup(io) io end + CLASS_NEW = Class.instance_method(:new) + def self.create_pipe(read_class, write_class, external = nil, internal = nil, options = nil) fds = Truffle::FFI::MemoryPointer.new(:int, 2) do |ptr| res = Truffle::POSIX.pipe(ptr) @@ -106,8 +108,8 @@ def self.create_pipe(read_class, write_class, external = nil, internal = nil, op ptr.read_array_of_int(2) end - lhs = pipe_end_setup(read_class.new(fds[0], IO::RDONLY)) - rhs = pipe_end_setup(write_class.new(fds[1], IO::WRONLY)) + lhs = pipe_end_setup(CLASS_NEW.bind_call(read_class, fds[0], IO::RDONLY)) + rhs = pipe_end_setup(CLASS_NEW.bind_call(write_class, fds[1], IO::WRONLY)) lhs.set_encoding external || Encoding.default_external, internal || Encoding.default_internal, options