|
5 | 5 | */
|
6 | 6 | package json.ext;
|
7 | 7 |
|
| 8 | +import org.jcodings.Encoding; |
8 | 9 | import org.jcodings.specific.UTF8Encoding;
|
9 | 10 | import org.jruby.Ruby;
|
10 | 11 | import org.jruby.RubyArray;
|
|
15 | 16 | import org.jruby.RubyFixnum;
|
16 | 17 | import org.jruby.RubyFloat;
|
17 | 18 | import org.jruby.RubyHash;
|
| 19 | +import org.jruby.RubyIO; |
18 | 20 | import org.jruby.RubyString;
|
19 | 21 | import org.jruby.RubySymbol;
|
20 | 22 | import org.jruby.runtime.Helpers;
|
@@ -81,11 +83,41 @@ static <T extends IRubyObject> RubyString generateJson(ThreadContext context, T
|
81 | 83 | return handler.generateNew(context, session, object);
|
82 | 84 | }
|
83 | 85 |
|
84 |
| - BufferedOutputStream buffer = new BufferedOutputStream(new IOOutputStream(io), IO_BUFFER_SIZE); |
| 86 | + BufferedOutputStream buffer = |
| 87 | + new BufferedOutputStream( |
| 88 | + new PatchedIOOutputStream(io, UTF8Encoding.INSTANCE), |
| 89 | + IO_BUFFER_SIZE); |
85 | 90 | handler.generateToBuffer(context, session, object, buffer);
|
86 | 91 | return io;
|
87 | 92 | }
|
88 | 93 |
|
| 94 | + /** |
| 95 | + * A version of IOOutputStream hacked to avoid fast-path RubyIO calls when the target IO has an external encoding. |
| 96 | + * |
| 97 | + * All calls to the underlying IO will be done dynamically and all incoming bytes wrapped in RubyString instances. |
| 98 | + * This avoids bugs in the fast-path logic in JRuby 9.4.12.0 and earlier that fails to properly handle writing bytes |
| 99 | + * when the source and target destination are the same. |
| 100 | + * |
| 101 | + * See https://github.com/jruby/jruby/issues/8682 |
| 102 | + */ |
| 103 | + private static class PatchedIOOutputStream extends IOOutputStream { |
| 104 | + public PatchedIOOutputStream(IRubyObject io, Encoding encoding) { |
| 105 | + super(io, encoding); |
| 106 | + } |
| 107 | + |
| 108 | + @Override |
| 109 | + public RubyIO getRealIO(IRubyObject io) { |
| 110 | + RubyIO realIO = super.getRealIO(io); |
| 111 | + |
| 112 | + // if the real IO has an external encoding, don't use fast path |
| 113 | + if (realIO == null || realIO.getEnc() != null) { |
| 114 | + return null; |
| 115 | + } |
| 116 | + |
| 117 | + return realIO; |
| 118 | + } |
| 119 | + } |
| 120 | + |
89 | 121 | /**
|
90 | 122 | * Returns the best serialization handler for the given object.
|
91 | 123 | */
|
|
0 commit comments