Skip to content

Commit 3a5dda7

Browse files
committed
Fix gets on Windows. Fixes #2959
* Don't unwrap stdin streams; the direct FileChannel does not seem to be usable and reads result in IOException: Not enough storage is available to process this command. * Tweaks for argf logic. * Always treat FileChannel as ready for read, since files always select immediately and processes can't be selected without native support.
1 parent f37dc99 commit 3a5dda7

File tree

4 files changed

+21
-35
lines changed

4 files changed

+21
-35
lines changed

core/src/main/java/org/jruby/RubyArgsFile.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.io.IOException;
3737
import static org.jruby.RubyEnumerator.enumeratorize;
3838

39+
import org.jruby.anno.FrameField;
3940
import org.jruby.anno.JRubyMethod;
4041
import org.jruby.exceptions.RaiseException;
4142
import jnr.posix.FileStat;
@@ -239,6 +240,7 @@ public static IRubyObject external_encoding(ThreadContext context, IRubyObject r
239240
return ((RubyIO) getData(context, recv, "no stream to set encoding").currentFile).external_encoding(context);
240241
}
241242

243+
// MRI: argf_getline
242244
private static IRubyObject argf_getline(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
243245
ArgsFileData data = ArgsFileData.getDataFrom(recv);
244246

@@ -269,21 +271,10 @@ private static IRubyObject argf_getline(ThreadContext context, IRubyObject recv,
269271
/** Read a line.
270272
*
271273
*/
272-
@JRubyMethod(name = "gets", optional = 1)
274+
@JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
273275
public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
274-
ArgsFileData data = ArgsFileData.getDataFrom(recv);
275-
276-
if (!data.next_argv(context)) return context.runtime.getNil();
277-
278-
IRubyObject line;
279-
if (!(data.currentFile instanceof RubyIO)) {
280-
line = data.currentFile.callMethod(context, "gets", args);
281-
} else {
282-
line = argf_getline(context, recv, args);
283-
}
284-
276+
IRubyObject line = argf_getline(context, recv, args);
285277
context.setLastLine(line);
286-
context.runtime.getGlobalVariables().set("$_", line);
287278

288279
return line;
289280
}

core/src/main/java/org/jruby/RubyGlobal.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -286,19 +286,9 @@ private static Channel prepareStdioChannel(Ruby runtime, STDIO stdio, Object str
286286
} else {
287287
switch (stdio) {
288288
case IN:
289-
stream = ShellLauncher.unwrapFilterInputStream((InputStream)stream);
290-
if (stream instanceof FileInputStream) {
291-
return ((FileInputStream)stream).getChannel();
292-
}
293-
294289
return Channels.newChannel((InputStream)stream);
295290
case OUT:
296291
case ERR:
297-
stream = ShellLauncher.unwrapFilterOutputStream((OutputStream)stream);
298-
if (stream instanceof FileOutputStream) {
299-
return ((FileOutputStream)stream).getChannel();
300-
}
301-
302292
return Channels.newChannel((OutputStream)stream);
303293
default: throw new RuntimeException("invalid stdio: " + stdio);
304294
}

core/src/main/java/org/jruby/RubyKernel.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,15 @@ public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
318318
return defin.callMethod(context, "getc");
319319
}
320320

321+
// MRI: rb_f_gets
321322
@JRubyMethod(optional = 1, module = true, visibility = PRIVATE)
322323
public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
323-
return RubyArgsFile.gets(context, context.runtime.getArgsFile(), args);
324+
Ruby runtime = context.runtime;
325+
326+
if (recv == runtime.getArgsFile()) {
327+
return RubyArgsFile.gets(context, runtime.getArgsFile(), args);
328+
}
329+
return runtime.getArgsFile().callMethod(context, "gets", args);
324330
}
325331

326332
@JRubyMethod(optional = 1, module = true, visibility = PRIVATE)

core/src/main/java/org/jruby/util/io/OpenFile.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,18 +1398,17 @@ boolean waitReadable(ThreadContext context, ChannelFD fd) {
13981398
}
13991399
}
14001400

1401-
// kinda-hacky way to see if there's more data to read from a seekable channel
1401+
/*
1402+
Seekable channels (usually FileChannel) are treated as ready always. There are
1403+
three kinds we typically see:
1404+
1. files, which always select(2) as ready
1405+
2. stdio, which we can't select and can't check .size for available data
1406+
3. subprocess stdio, which we can't select and can't check .size either
1407+
In all three cases, without native fd logic, we can't do anything to determine
1408+
if the stream is ready, so we just assume it is and hope for the best.
1409+
*/
14021410
if (fd.chSeek != null) {
1403-
FileChannel fdSeek = fd.chSeek;
1404-
try {
1405-
// not a real file, can't get size...we'll have to just read and block
1406-
if (fdSeek.size() < 0) return true;
1407-
1408-
// if current position is less than file size, read should not block
1409-
return fdSeek.position() < fdSeek.size();
1410-
} catch (IOException ioe) {
1411-
throw context.runtime.newIOErrorFromException(ioe);
1412-
}
1411+
return true;
14131412
}
14141413
} finally {
14151414
if (locked) unlock();

0 commit comments

Comments
 (0)