@@ -381,12 +381,18 @@ private static IRubyObject argf_getline(ThreadContext context, final int callInf
381381 while (true ) {
382382 if (!data .next_argv (context )) return context .nil ;
383383
384- RubyIO currentFile = (RubyIO ) data .currentFile ;
385-
386384 if (isGenericInput (context , data )) {
387385 context .callInfo = callInfo ; // restore callInfo for kwargs
388- line = data .currentFile .callMethod (context , "gets" , args );
386+ line = sites (context ).gets .call (context , argsFile (context ), data .currentFile , args );
387+
388+ // An IO updates the line number itself, but other objects do not
389+ if (!line .isNil () && !(data .currentFile instanceof RubyIO )) {
390+ context .runtime .setCurrentLine (context .runtime .getCurrentLine () + 1 );
391+ }
389392 } else {
393+ // $stdin may be any object with gets (e.g. StringIO), so only cast once we know it is not generic input
394+ RubyIO currentFile = (RubyIO ) data .currentFile ;
395+
390396 if (args .length == 0 && context .runtime .getRecordSeparatorVar ().get () == globalVariables (context ).getDefaultSeparator ()) {
391397 line = (currentFile ).gets (context );
392398 } else {
@@ -416,38 +422,48 @@ private static boolean isGenericInput(ThreadContext context, ArgsFileData data)
416422 /** Read a line.
417423 *
418424 */
419- @ JRubyMethod (name = "gets" , optional = 1 , keywords = true , checkArity = false , writes = LASTLINE )
425+ @ JRubyMethod (name = "gets" , optional = 2 , keywords = true , checkArity = false , writes = LASTLINE )
420426 public static IRubyObject gets (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
421427 final int callInfo = ThreadContext .resetCallInfo (context );
422- Arity . checkArgumentCount (context , args , 0 , 1 );
428+ checkGetlineArity (context , callInfo , args );
423429
424430 return context .setLastLine (argf_getline (context , callInfo , args ));
425431 }
426432
427433 /** Read a line.
428434 *
429435 */
430- @ JRubyMethod (name = "readline" , optional = 1 , keywords = true , checkArity = false , writes = LASTLINE )
436+ @ JRubyMethod (name = "readline" , optional = 2 , keywords = true , checkArity = false , writes = LASTLINE )
431437 public static IRubyObject readline (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
438+ final int callInfo = ThreadContext .resetCallInfo (context );
439+ ArgsFileData data = ArgsFileData .getArgsFileData (context .runtime );
440+
441+ // MRI: argf_readline forwards to $stdin.readline when $stdin is not an IO
442+ if (data .next_argv (context ) && !(data .currentFile instanceof RubyIO )) {
443+ context .callInfo = callInfo ; // restore callInfo for kwargs
444+ return sites (context ).readline .call (context , recv , data .currentFile , args );
445+ }
446+
447+ context .callInfo = callInfo ; // restore callInfo for kwargs
432448 IRubyObject line = gets (context , recv , args );
433449
434450 if (line .isNil ()) throw context .runtime .newEOFError ();
435451
436452 return line ;
437453 }
438454
439- @ JRubyMethod (optional = 1 , keywords = true , checkArity = false )
455+ @ JRubyMethod (optional = 2 , keywords = true , checkArity = false )
440456 public static IRubyObject readlines (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
441457 final int callInfo = ThreadContext .resetCallInfo (context );
442- Arity . checkArgumentCount (context , args , 0 , 1 );
458+ checkGetlineArity (context , callInfo , args );
443459
444460 ArgsFileData data = ArgsFileData .getArgsFileData (context .runtime );
445461
446462 if (!data .next_argv (context )) return newEmptyArray (context );
447463
448464 if (!(data .currentFile instanceof RubyIO )) {
449- // TODO do we need to restore callInfo here?
450- return data . currentFile . callMethod (context , "readlines" , args );
465+ context . callInfo = callInfo ; // restore callInfo for kwargs
466+ return sites ( context ). readlines . call (context , recv , data . currentFile , args );
451467 }
452468
453469 var ary = newArray (context );
@@ -458,21 +474,15 @@ public static IRubyObject readlines(ThreadContext context, IRubyObject recv, IRu
458474 return ary ;
459475 }
460476
461- @ JRubyMethod (optional = 1 , checkArity = false )
477+ // MRI: to_a is an alias of readlines
478+ @ JRubyMethod (optional = 2 , keywords = true , checkArity = false )
462479 public static IRubyObject to_a (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
463- Arity .checkArgumentCount (context , args , 0 , 1 );
464-
465- ArgsFileData data = ArgsFileData .getArgsFileData (context .runtime );
466-
467- if (!data .next_argv (context )) return newEmptyArray (context );
468- if (!(data .currentFile instanceof RubyIO )) return data .currentFile .callMethod (context , "to_a" , args );
480+ return readlines (context , recv , args );
481+ }
469482
470- var ary = newArray (context );
471- IRubyObject line ;
472- while ((line = argf_getline (context , 0 , args )) != context .nil ) {
473- ary .append (context , line );
474- }
475- return ary ;
483+ // MRI: rb_io_getline takes up to two positional arguments (separator, limit) plus keywords
484+ private static void checkGetlineArity (ThreadContext context , int callInfo , IRubyObject [] args ) {
485+ Arity .checkArgumentCount (context , args .length - (hasKeywords (callInfo ) ? 1 : 0 ), 0 , 2 );
476486 }
477487
478488 @ JRubyMethod
@@ -570,21 +580,25 @@ public static IRubyObject codepoints(ThreadContext context, IRubyObject recv, Bl
570580 /** Invoke a block for each line.
571581 *
572582 */
573- @ JRubyMethod (name = "each_line" , optional = 1 , keywords = true , checkArity = false )
583+ @ JRubyMethod (name = "each_line" , optional = 2 , keywords = true , checkArity = false )
574584 public static IRubyObject each_line (ThreadContext context , IRubyObject recv , IRubyObject [] args , Block block ) {
575- if (!block .isGiven ()) return enumeratorize (context .runtime , recv , "each_line" , args );
576-
577585 final int callInfo = ThreadContext .resetCallInfo (context );
578- Arity .checkArgumentCount (context , args , 0 , 1 );
586+ if (!block .isGiven ()) return enumeratorize (context .runtime , recv , "each_line" , args , hasKeywords (callInfo ));
587+
588+ return eachLineCommon (context , recv , callInfo , args , block );
589+ }
590+
591+ private static IRubyObject eachLineCommon (ThreadContext context , IRubyObject recv , int callInfo , IRubyObject [] args , Block block ) {
592+ checkGetlineArity (context , callInfo , args );
579593
580594 ArgsFileData data = ArgsFileData .getArgsFileData (context .runtime );
581595
582596 if (!data .next_argv (context )) return context .nil ;
583597
598+ // MRI: argf_each_line calls each_line on the current file with the same arguments
584599 if (!(data .currentFile instanceof RubyIO )) {
585- if (!data .next_argv (context )) return recv ;
586-
587- data .currentFile .callMethod (context , "each" , NULL_ARRAY , block );
600+ context .callInfo = callInfo ; // restore callInfo for kwargs
601+ data .currentFile .callMethod (context , "each_line" , args , block );
588602 data .next_p = NextFile ;
589603 }
590604
@@ -602,9 +616,12 @@ public static IRubyObject lines(ThreadContext context, IRubyObject recv, IRubyOb
602616 return each_line (context , recv , args , block );
603617 }
604618
605- @ JRubyMethod (name = "each" , optional = 1 , checkArity = false )
619+ @ JRubyMethod (name = "each" , optional = 2 , keywords = true , checkArity = false )
606620 public static IRubyObject each (final ThreadContext context , IRubyObject recv , IRubyObject [] args , final Block block ) {
607- return block .isGiven () ? each_line (context , recv , args , block ) : enumeratorize (context .runtime , recv , "each" , args );
621+ final int callInfo = ThreadContext .resetCallInfo (context );
622+ if (!block .isGiven ()) return enumeratorize (context .runtime , recv , "each" , args , hasKeywords (callInfo ));
623+
624+ return eachLineCommon (context , recv , callInfo , args , block );
608625 }
609626
610627 @ JRubyMethod (name = "file" )
0 commit comments