Skip to content

Commit 8146a9b

Browse files
committed
Refactor specific-arity JIT kwargs logic
This follows the refactor of variable-arity varargs logic, shared by both JIT and interpreter. With specific arity, we never have an action that replaces the incoming argument, nor do we return UNDEFINED or clear the ruby2_keywords_hash flag.
1 parent fdfa2a5 commit 8146a9b

1 file changed

Lines changed: 42 additions & 83 deletions

File tree

core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java

Lines changed: 42 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)