@@ -703,65 +703,46 @@ public static IRubyObject undefined() {
703703 // call on this path and pass in the live value of ruby2_keywords from the scope.
704704 @ JIT
705705 public static IRubyObject receiveSpecificArityKeywords (ThreadContext context , IRubyObject last , boolean ruby2Keywords ) {
706- if (!(last instanceof RubyHash )) {
707- ThreadContext .clearCallInfo (context );
708- return last ;
706+ int callInfo = resetCallInfo (context );
707+ if (last instanceof RubyHash hash ) {
708+ KwargsAction kwargsAction = kwargsActionJIT (last , ruby2Keywords , callInfo );
709+ return switch (kwargsAction ) {
710+ case RETURN_DUP -> hash .dupFast (context );
711+ case RETURN_DUP_WITH_RUBY2 -> hash .dupFast (context ).withRuby2Keywords (true );
712+ case RETURN_WITHOUT_RUBY2 -> hash .withRuby2Keywords (false );
713+ case RETURN_HASH -> hash ;
714+
715+ // JIT does not have these conditions
716+ default -> throw new IllegalArgumentException ("null kwargs action" );
717+ };
709718 }
710-
711- return ruby2Keywords ?
712- receiveSpecificArityRuby2HashKeywords (context , (RubyHash ) last ) :
713- receiveSpecificArityHashKeywords (context , (RubyHash ) last );
719+ return last ;
714720 }
715721
716- private static IRubyObject receiveSpecificArityHashKeywords (ThreadContext context , RubyHash last ) {
717- int callInfo = ThreadContext .resetCallInfo (context );
718- boolean isKwarg = (callInfo & CALL_KEYWORD ) != 0 ;
722+ public static KwargsAction kwargsActionJIT (IRubyObject last , boolean ruby2Keywords , int callInfo ) {
723+ boolean isKwarg = hasKeywords (callInfo );
724+ if (ruby2Keywords ) {
725+ // ruby2_keywords only get unmarked if it enters a method which accepts keywords.
726+ // This means methods which don't just keep that marked hash around in case it is passed
727+ // onto another method which accepts keywords.
728+ if (isKwarg ) {
729+ // a ruby2_keywords method which happens to receive a keyword. Mark hash as ruby2_keyword
730+ // So it can be used similarly to an ordinary hash passed in this way.
731+ return KwargsAction .RETURN_DUP_WITH_RUBY2 ;
732+ }
719733
720- if ((callInfo & CALL_SPLATS ) != 0 && last .isRuby2KeywordHash ()) {
721- return last .dupFast (context );
722- }
734+ return KwargsAction .RETURN_HASH ;
735+ } else {
736+ if ((callInfo & CALL_SPLATS ) != 0 && ((RubyHash ) last ).isRuby2KeywordHash ()) {
737+ return KwargsAction .RETURN_DUP ;
738+ }
723739
724- return isKwarg ?
725- last .dupFast (context ) :
726- last ;
727- }
740+ if (isKwarg ) {
741+ return KwargsAction .RETURN_DUP ;
742+ }
728743
729- private static IRubyObject receiveSpecificArityRuby2HashKeywords (ThreadContext context , RubyHash last ) {
730- int callInfo = ThreadContext .resetCallInfo (context );
731- boolean isKwarg = (callInfo & CALL_KEYWORD ) != 0 ;
732-
733- // ruby2_keywords only get unmarked if it enters a method which accepts keywords.
734- // This means methods which don't just keep that marked hash around in case it is passed
735- // onto another method which accepts keywords.
736- if (isKwarg ) {
737- // a ruby2_keywords method which happens to receive a keyword. Mark hash as ruby2_keyword
738- // So it can be used similarly to an ordinary hash passed in this way.
739- RubyHash hash = last .dupFast (context );
740- hash .setRuby2KeywordHash (true );
741- return hash ;
744+ return KwargsAction .RETURN_HASH ;
742745 }
743-
744- return last ;
745- }
746-
747- /**
748- * Simplified receiveKeywords when:
749- * receiver is not a ruby2_keywords method.
750- * receiver does not accept keywords.
751- * there's no rest argument.
752- *
753- * @param context
754- * @param args
755- * @return the prepared kwargs hash, or UNDEFINED as a sigil for no kwargs
756- */
757- @ JIT
758- public static IRubyObject receiveNormalKeywordsNoRestNoKeywords (ThreadContext context , IRubyObject [] args ) {
759- int callInfo = ThreadContext .resetCallInfo (context );
760- if (shouldHandleKwargs (args , callInfo ) && (callInfo & CALL_SPLATS ) != 0 ) {
761- return receiveKeywordsWithSplatsNoRestNoKeywords (context , args );
762- }
763-
764- return UNDEFINED ;
765746 }
766747
767748 /**
@@ -795,29 +776,6 @@ public static IRubyObject receiveKeywords(ThreadContext context, IRubyObject[] a
795776 return UNDEFINED ;
796777 }
797778
798- private static IRubyObject receiveKeywordsWithSplatsNoRestNoKeywords (ThreadContext context , IRubyObject [] args ) {
799- RubyHash hash = (RubyHash ) args [args .length - 1 ];
800-
801- if (hash .isRuby2KeywordHash ()) {
802- if (hash .isEmpty ()) {
803- // case where we somehow (hash.clear) a marked ruby2_keyword. We pass it as keyword even in non-keyword
804- // accepting methods so it is subtracted from the arity count. Normally empty keyword arguments are not
805- // passed along but ruby2_keyword is a strange case since it is mutable by users.
806- return hash ;
807- }
808-
809- clearTrailingHashRuby2Keywords (context , args , hash );
810- }
811-
812- return UNDEFINED ;
813- }
814-
815- private static void clearTrailingHashRuby2Keywords (ThreadContext context , IRubyObject [] args , RubyHash hash ) {
816- RubyHash newHash = hash .dupFast (context );
817- newHash .setRuby2KeywordHash (false );
818- args [args .length - 1 ] = newHash ;
819- }
820-
821779 private static boolean shouldHandleKwargs (IRubyObject [] args , int callInfo ) {
822780 return (callInfo & CALL_KEYWORD_EMPTY ) == 0 && args .length >= 1 && args [args .length - 1 ] instanceof RubyHash ;
823781 }
@@ -870,37 +828,38 @@ private static IRubyObject receiveKeywordsHash(ThreadContext context, IRubyObjec
870828
871829 private static KwargsAction kwargsAction (RubyHash hash , boolean hasRestArgs ,
872830 boolean acceptsKeywords , boolean ruby2_keywords_method , int callInfo ) {
831+ boolean empty = hash .isEmpty ();
832+
873833 if (ruby2_keywords_method ) {
874834 // no keywords to use in this method
875- if ((callInfo & CALL_KEYWORD ) != 0 ) {
835+ if (hasKeywords (callInfo ) ) {
876836 return KwargsAction .REPLACE_WITH_DUP_WITH_RUBY2_RETURN_UNDEFINED ;
877- } else if (hash .isRuby2KeywordHash () && (callInfo & CALL_SPLATS ) != 0 && hash . isEmpty () ) { // splatted empty hash...just return it
837+ } else if (hash .isRuby2KeywordHash () && (callInfo & CALL_SPLATS ) != 0 && empty ) { // splatted empty hash...just return it
878838 return KwargsAction .RETURN_HASH ;
879- } else if (acceptsKeywords && !hash . isEmpty () ) { // keywords method which has keywords
839+ } else if (acceptsKeywords && !empty ) { // keywords method which has keywords
880840 return KwargsAction .RETURN_DUP ;
881841 } else {
882842 return KwargsAction .RETURN_UNDEFINED ;
883843 }
884844 } else if (hash .isRuby2KeywordHash ()) {
885- RubyHash hash1 = hash ;
886845 // ruby2_keywords only get unmarked if it enters a method which accepts keywords.
887846 // This means methods which don't just keep that marked hash around in case it is passed
888847 // onto another method which accepts keywords.
889848 if (acceptsKeywords ) {
890- if (!hash1 . isEmpty () ) {
849+ if (!empty ) {
891850 return KwargsAction .RETURN_DUP_WITHOUT_RUBY2 ;
892851 } else {
893852 return KwargsAction .RETURN_WITHOUT_RUBY2 ;
894853 }
895854 } else if ((callInfo & CALL_SPLATS ) != 0 ) {
896855 // somehow we splatted an array with an empty keyword argument attached (ruby2_keywords_hash can lead to this)
897- if (hash1 . isEmpty () ) {
856+ if (empty ) {
898857 return KwargsAction .RETURN_HASH ;
899858 } else {
900859 return KwargsAction .REPLACE_WITH_DUP_RETURN_UNDEFINED ;
901860 }
902861 } else if (hasKeywords (callInfo )) {
903- if (!hash1 . isEmpty () ) {
862+ if (!empty ) {
904863 return KwargsAction .RETURN_DUP ;
905864 } else if (hasRestArgs ) {
906865 // FIXME: This is a bit gross. a real kwarg callsite if passed to a non-kwarg method but it
@@ -916,7 +875,7 @@ private static KwargsAction kwargsAction(RubyHash hash, boolean hasRestArgs,
916875 }
917876 } else if (!hasKeywords (callInfo )) {
918877 return KwargsAction .RETURN_UNDEFINED ;
919- } else if (acceptsKeywords && !hash . isEmpty () ) {
878+ } else if (acceptsKeywords && !empty ) {
920879 return KwargsAction .RETURN_DUP ;
921880 } else {
922881 return KwargsAction .REPLACE_WITH_DUP_RETURN_UNDEFINED ;
0 commit comments