Skip to content

Commit 7df4773

Browse files
committed
[GR-16973] [GR-16975] Fix IO#write to not modify the argument when the encoding does not match.
PullRequest: truffleruby/934
2 parents e28a849 + 5da207b commit 7df4773

File tree

6 files changed

+54
-3
lines changed

6 files changed

+54
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Bug fixes:
55
* Set `RbConfig::CONFIG['ruby_version']` to the same value as the TruffleRuby version. This fixes reusing C extensions between different versions of TruffleRuby with Bundler (#1715).
66
* Fixed `Symbol#match` returning `MatchData` (#1706).
77
* Allow `Time#strftime` to be called with binary format strings.
8+
* Do not modify the argument passed to `IO#write` when the encoding does not match (#1714).
89

910
Compatibility:
1011

spec/ruby/core/io/flush_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,33 @@
55
it "raises IOError on closed stream" do
66
lambda { IOSpecs.closed_io.flush }.should raise_error(IOError)
77
end
8+
9+
describe "on a pipe" do
10+
before :each do
11+
@r, @w = IO.pipe
12+
end
13+
14+
after :each do
15+
@r.close
16+
begin
17+
@w.close
18+
rescue Errno::EPIPE
19+
end
20+
end
21+
22+
# [ruby-core:90895] MJIT worker may leave fd open in a forked child.
23+
# For instance, MJIT creates a worker before @r.close with fork(), @r.close happens,
24+
# and the MJIT worker keeps the pipe open until the worker execve().
25+
# TODO: consider acquiring GVL from MJIT worker.
26+
guard_not -> { defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? } do
27+
it "raises Errno::EPIPE if sync=false and the read end is closed" do
28+
@w.sync = false
29+
@w.write "foo"
30+
@r.close
31+
32+
-> { @w.flush }.should raise_error(Errno::EPIPE, /Broken pipe/)
33+
-> { @w.close }.should raise_error(Errno::EPIPE, /Broken pipe/)
34+
end
35+
end
36+
end
837
end

spec/ruby/core/io/shared/write.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@
6969
lambda { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError)
7070
end
7171

72+
it "does not modify the passed argument" do
73+
File.open(@filename, "w") do |f|
74+
f.set_encoding(Encoding::IBM437)
75+
# A character whose codepoint differs between UTF-8 and IBM437
76+
f.write \n".freeze
77+
end
78+
79+
File.binread(@filename).bytes.should == [159, 10]
80+
end
81+
7282
describe "on a pipe" do
7383
before :each do
7484
@r, @w = IO.pipe

spec/ruby/library/tempfile/initialize_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@
3838
@tempfile.send(:initialize, ['shiftjis', 'yml'], encoding: 'SHIFT_JIS')
3939
@tempfile.external_encoding.should == Encoding::Shift_JIS
4040
end
41+
42+
it "does not try to modify the arguments" do
43+
@tempfile.send(:initialize, ['frozen'.freeze, 'txt'.freeze], encoding: Encoding::IBM437)
44+
@tempfile.external_encoding.should == Encoding::IBM437
45+
end
4146
end

src/main/ruby/truffleruby/core/io.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2549,7 +2549,7 @@ def write(data)
25492549
external_encoding != data.encoding &&
25502550
external_encoding != Encoding::ASCII_8BIT
25512551
unless data.ascii_only? && external_encoding.ascii_compatible?
2552-
data.encode!(external_encoding)
2552+
data = data.encode(external_encoding)
25532553
end
25542554
end
25552555

src/main/ruby/truffleruby/core/post.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,17 @@ def external_encoding
6060
STDERR.sync = true
6161

6262
# Always flush standard streams on exit
63-
Truffle::KernelOperations.at_exit true do
63+
Truffle::KernelOperations.at_exit(true) do
6464
STDOUT.flush
65+
rescue Errno::EPIPE
66+
# Could not write to STDOUT, the calling process closed the pipe
67+
nil
6568
end
66-
Truffle::KernelOperations.at_exit true do
69+
Truffle::KernelOperations.at_exit(true) do
6770
STDERR.flush
71+
rescue Errno::EPIPE
72+
# Could not write to STDERR, the calling process closed the pipe
73+
nil
6874
end
6975

7076
module Truffle

0 commit comments

Comments
 (0)